Index: conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
retrieving revision 1.592
diff -u -p -r1.592 files
--- conf/files  11 May 2015 06:46:21 -0000      1.592
+++ conf/files  24 May 2015 12:29:00 -0000
@@ -517,6 +517,8 @@ attach      softraid at root
 file   dev/softraid.c                  softraid        needs-flag
 file   dev/softraid_concat.c           softraid
 file   dev/softraid_crypto.c           softraid & crypto
+file   dev/softraid_luks.c             softraid & crypto
+file   dev/luks.c                      softraid & crypto
 file   dev/softraid_raid0.c            softraid
 file   dev/softraid_raid1.c            softraid
 file   dev/softraid_raid5.c            softraid
@@ -686,7 +688,7 @@ file kern/kern_synch.c
 file kern/kern_tc.c
 file kern/kern_time.c
 file kern/kern_timeout.c
-file kern/kern_uuid.c                  gpt
+file kern/kern_uuid.c                  gpt | (softraid & crypto)
 file kern/kern_watchdog.c              !small_kernel
 file kern/kern_task.c
 file kern/kern_xxx.c
Index: crypto/xform.c
===================================================================
RCS file: /cvs/src/sys/crypto/xform.c,v
retrieving revision 1.46
diff -u -p -r1.46 xform.c
--- crypto/xform.c      14 Mar 2015 03:38:46 -0000      1.46
+++ crypto/xform.c      24 May 2015 12:02:30 -0000
@@ -18,6 +18,8 @@
  *
  * AES XTS implementation in 2008 by Damien Miller
  *
+ * PBKDF2: Copyright (c) 2008 Damien Bergamini <damien.bergam...@free.fr>
+ *
  * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis,
  * Angelos D. Keromytis and Niels Provos.
  *
@@ -46,6 +48,8 @@
 #include <sys/errno.h>
 #include <sys/time.h>
 #include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/stdint.h>
 #include <machine/cpu.h>
 
 #include <crypto/md5.h>
@@ -58,6 +62,7 @@
 #include <crypto/cryptodev.h>
 #include <crypto/xform.h>
 #include <crypto/gmac.h>
+#include <crypto/hmac.h>
 
 extern void des_ecb3_encrypt(caddr_t, caddr_t, caddr_t, caddr_t, caddr_t, int);
 extern void des_ecb_encrypt(caddr_t, caddr_t, caddr_t, int);
@@ -675,4 +680,64 @@ lzs_dummy(u_int8_t *data, u_int32_t size
 {
        *out = NULL;
        return (0);
+}
+
+/*
+ * Password-Based Key Derivation Function 2 (PKCS #5 v2.0).
+ * Code based on IEEE Std 802.11-2007, Annex H.4.2.
+ */
+int
+pkcs5_pbkdf2(const char *pass, size_t pass_len, const char *salt,
+    size_t salt_len, u_int8_t *key, size_t key_len, u_int rounds)
+{
+       HMAC_SHA1_CTX ctx;
+       u_int8_t *asalt, obuf[SHA1_DIGEST_LENGTH];
+       u_int8_t d1[SHA1_DIGEST_LENGTH], d2[SHA1_DIGEST_LENGTH];
+       u_int i, j;
+       u_int count;
+       size_t r;
+
+       if (rounds < 1 || key_len == 0)
+               return -1;
+       if (salt_len == 0 || salt_len > SIZE_MAX - 1)
+               return -1;
+       asalt = malloc(salt_len + 4, M_TEMP, M_NOWAIT);
+       if (asalt == NULL)
+               return -1;
+
+       memcpy(asalt, salt, salt_len);
+
+       for (count = 1; key_len > 0; count++) {
+               asalt[salt_len + 0] = (count >> 24) & 0xff;
+               asalt[salt_len + 1] = (count >> 16) & 0xff;
+               asalt[salt_len + 2] = (count >> 8) & 0xff;
+               asalt[salt_len + 3] = count & 0xff;
+               HMAC_SHA1_Init(&ctx, pass, pass_len);
+               HMAC_SHA1_Update(&ctx, asalt, salt_len + 4);
+               HMAC_SHA1_Final(d1, &ctx);
+               memcpy(obuf, d1, sizeof(obuf));
+
+               for (i = 1; i < rounds; i++) {
+                       HMAC_SHA1_Init(&ctx, pass, pass_len);
+                       HMAC_SHA1_Update(&ctx, d1, sizeof d1);
+                       HMAC_SHA1_Final(d2, &ctx);
+
+                       memcpy(d1, d2, sizeof(d1));
+                       for (j = 0; j < sizeof(obuf); j++)
+                               obuf[j] ^= d1[j];
+               }
+
+               r = MIN(key_len, SHA1_DIGEST_LENGTH);
+               memcpy(key, obuf, r);
+               key += r;
+               key_len -= r;
+       };
+       explicit_bzero(asalt, salt_len + 4);
+       explicit_bzero(d1, sizeof(d1));
+       explicit_bzero(d2, sizeof(d2));
+       explicit_bzero(obuf, sizeof(obuf));
+       explicit_bzero(&ctx, sizeof ctx);
+       free(asalt, M_TEMP, salt_len + 4);
+
+       return 0;
 }
Index: dev/luks.c
===================================================================
RCS file: dev/luks.c
diff -N dev/luks.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/luks.c  24 May 2015 12:02:30 -0000
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2015 Martin Pelikan <peli...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <lib/libsa/aes_xts.h>
+#include <lib/libsa/pbkdf2.h>
+#include <crypto/cryptodev.h>
+#include <crypto/sha1.h>
+
+#include <dev/softraidvar.h>
+#include <dev/softraid_luks.h>
+#include <dev/luks.h>
+
+#define        LUKS_INFLATED_KEY_MAX   (4 * 1024 * 1024)
+
+static const u_int8_t luks_magic[] = LUKS_MAGIC;
+struct luks_cipher luks_ciphers[] = {
+       { "aes",        "xts-plain64",  CRYPTO_AES_XTS, 128 * 2, 256 * 2 },
+       { "blowfish",   "cbc-plain",    CRYPTO_BLF_CBC, 32, 256 },
+};
+struct luks_hash luks_hashes[] = {
+       { "sha1",       CRYPTO_SHA1 },
+};
+
+/*
+ * It's now obvious where that "implementation error" came from.
+ * H1 only reads i-th hunk to compute i-th hash and therefore
+ * can be done in place.  H2 needs the whole stripe all the time.
+ * Because neither SHA1 nor XOR touch bits where they don't operate,
+ * the repeated-H1 process is parallelizable across the hunks while
+ * the repeated-H2 has to wait before it uses the next stripe.
+ */
+static void
+H1(u_int8_t *stripe, size_t len)
+{
+       u_int8_t digest[SHA1_DIGEST_LENGTH];
+       const size_t blocks = len / sizeof digest;
+       const size_t oddblk = len % sizeof digest;
+       u_int32_t i, i_be;
+       SHA1_CTX shactx;
+
+       for (i = 0; i < blocks; ++i) {
+               u_int8_t *hunk = stripe + (i * sizeof digest);
+
+               SHA1Init(&shactx);
+               i_be = htobe32(i);
+               SHA1Update(&shactx, (void *) &i_be, sizeof i_be);
+               SHA1Update(&shactx, hunk, sizeof digest);
+               SHA1Final(hunk, &shactx);
+       }
+
+       if (oddblk) {
+               u_int8_t *hunk = stripe + (blocks * sizeof digest);
+
+               SHA1Init(&shactx);
+               i_be = htobe32(i);
+               SHA1Update(&shactx, (void *) &i_be, sizeof i_be);
+               SHA1Update(&shactx, hunk, oddblk);
+               SHA1Final(digest, &shactx);
+               memcpy(hunk, digest, oddblk);
+       }
+}
+
+#ifdef notyet
+/*
+ * Obfuscate the master key before its encryption by making up (N - 1) stripes
+ * of random data, constructing a cryptographically safe dependency among them,
+ * XORing that last value with the key, then encrypt and store the whole thing.
+ */
+static void
+afsplit(u_int8_t *dst, const u_int8_t *src, size_t len, size_t stripes)
+{
+       /* Use the last stripe as temporary. */
+       u_int8_t *temp = dst + (len * (stripes - 1));
+       size_t i, n;
+
+       /* s_1 ... s_{n-1} randomly chosen */
+       arc4random_buf(dst, temp - dst);
+
+       /* d_0 = 0 */
+       memset(temp, 0, len);
+
+       /* s-indexes go from 1, but this loop and d-indexes go from 0 */
+       for (n = 0; n < stripes - 1; ++n) {
+               u_int8_t *current = dst + (len * n);
+
+               /* d_{k-1} ^ s_k */
+               for (i = 0; i < len; ++i)
+                       temp[i] ^= current[i];
+
+               /* d_k = H(last_step) */
+               H1(temp, len);
+       }
+
+       /* s_n = d_{n-1} ^ D */
+       for (i = 0; i < len; ++i)
+               temp[i] ^= src[i];
+}
+#endif /* notyet */
+
+/*
+ * De-obfuscate the master key material and return the key itself.
+ * Note that the last de-obfuscation stripe is constructed in the
+ * same manner as above.  Code duplicated and renamed for clarity.
+ */
+static void
+afmerge(u_int8_t * __restrict key, const u_int8_t * __restrict src, size_t len,
+    size_t stripes)
+{
+       size_t i, n;
+
+       /* d_0 = 0 */
+       memset(key, 0, len);
+
+       /* s-indexes go from 1, but this loop and d-indexes go from 0 */
+       for (n = 0; n < stripes - 1; ++n) {
+               const u_int8_t *current = src + (len * n);
+
+               /* d_{k-1} ^ s_k */
+               for (i = 0; i < len; ++i)
+                       key[i] ^= current[i];
+
+               /* d_k = H(last_step) */
+               H1(key, len);
+       }
+
+       /* D = d_{n-1} ^ s_n */
+       src += len * (stripes - 1);
+       for (i = 0; i < len; ++i)
+               key[i] ^= src[i];
+}
+
+int
+luks_deobfuscate_key(struct luks_phdr *luks, void *masterkey, void *userkey,
+    void *userkdf, size_t bytes, size_t stripes)
+{
+       u_int8_t                 digest[LUKS_DIGEST_LEN];
+
+       afmerge(masterkey, userkey, bytes, stripes);
+
+       /* XXX will need to be replaced to support non-SHA1 hashes. */
+       if (pkcs5_pbkdf2(masterkey, bytes,
+           luks->master_key_pbkdf2_salt, LUKS_SALT_LEN,
+           digest, sizeof digest,
+           betoh32(luks->master_key_pbkdf2_iterations)))
+               return (EPERM);
+
+       return (memcmp(luks->master_key_pbkdf2, digest, sizeof digest) ?
+           EPERM : 0);
+}
+
+static void
+luks_choose_cipher_hash(struct sr_luks *sl, u_int32_t keylen)
+{
+       struct luks_phdr *h = sl->sl_phdr;
+       int i;
+
+       /* Figure out if we can use the cipher and hash specified. */
+       for (i = 0; i < sizeof luks_ciphers / sizeof *luks_ciphers; ++i) {
+               if (strncmp(luks_ciphers[i].lc_name,
+                   h->cipher_name, sizeof h->cipher_name))
+                       continue;
+
+               if (strncmp(luks_ciphers[i].lc_mode,
+                   h->cipher_mode, sizeof h->cipher_mode))
+                       continue;
+
+               if (keylen * CHAR_BIT < luks_ciphers[i].lc_klen_min ||
+                   keylen * CHAR_BIT > luks_ciphers[i].lc_klen_max)
+                       continue;
+
+               sl->sl_alg = luks_ciphers[i].lc_alg;
+               sl->sl_keylen = keylen * CHAR_BIT;
+               break;
+       }
+
+       for (i = 0; i < sizeof luks_hashes / sizeof *luks_hashes; ++i) {
+               if (strncmp(luks_hashes[i].lh_name,
+                   h->hash_spec, sizeof h->hash_spec))
+                       continue;
+
+               sl->sl_hash = luks_hashes[i].lh_alg;
+               break;
+       }
+}
+
+const char *
+luks_phdr_parse_and_validate(struct sr_luks *sl, int64_t last_sector)
+{
+       struct luks_phdr *h = sl->sl_phdr;
+       u_int32_t payload_at = betoh32(h->payload_offset);
+       u_int32_t keylen = betoh32(h->master_key_bytes);
+       int i;
+
+       if (last_sector < 2)
+               return "disk too small";
+
+       if (memcmp(h->magic, luks_magic, sizeof luks_magic))
+               return "bad magic constant";
+
+       if (h->version != htobe16(1))
+               return "bad version";
+
+       if (payload_at > last_sector)
+               return "payload beyond end of disk";
+
+       if (keylen == 0)
+               return "zero key length";
+
+       luks_choose_cipher_hash(sl, keylen);
+       if (sl->sl_alg == 0 || sl->sl_hash == 0)
+               return "cipher or hash not supported";
+
+       /* Check all the key positions for sanity. */
+       for (i = 0; i < LUKS_NUM_KEYS; ++i) {
+               struct luks_key *k = h->keyblock + i;
+               int64_t key_at = betoh32(k->key_material_offset);
+               int64_t inflated = keylen * betoh32(k->stripes);
+
+               switch (betoh32(k->active)) {
+               case LUKS_KEY_ACTIVE_MAGIC:
+               case LUKS_KEY_INACTIVE_MAGIC:
+                       break;
+               default:
+                       return "key slot invalid";
+               }
+
+               if (inflated > LUKS_INFLATED_KEY_MAX)
+                       return "inflated key too big";
+               inflated /= DEV_BSIZE;
+               if (key_at + inflated > payload_at)
+                       return "inflated key beyond end of disk";
+       }
+
+       return (NULL);
+}
Index: dev/luks.h
===================================================================
RCS file: dev/luks.h
diff -N dev/luks.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/luks.h  24 May 2015 12:02:30 -0000
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2015 Martin Pelikan <peli...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+/*
+ * Linux Unified Key Setup on-disk data structure format, from the spec at:
+ * 
https://gitlab.com/cryptsetup/cryptsetup/wikis/LUKS-standard/on-disk-format.pdf
+ * Fields are big-endian.
+ */
+
+#define        LUKS_MAGIC              { 'L', 'U', 'K', 'S', 0xBA, 0xBE }
+#define        LUKS_MAGIC_LEN          6
+#define        LUKS_CIPHER_NAME_LEN    32
+#define        LUKS_CIPHER_MODE_LEN    32
+#define        LUKS_HASH_SPEC_LEN      32
+#define        LUKS_DIGEST_LEN         20
+#define        LUKS_SALT_LEN           32
+#define        LUKS_UUID_STRING_LEN    40
+
+#define        LUKS_NUM_KEYS           8
+#define        LUKS_KEY_ACTIVE_MAGIC   0x00AC71F3
+#define        LUKS_KEY_INACTIVE_MAGIC 0x0000DEAD
+
+struct luks_key {
+       u_int32_t       active;
+       u_int32_t       pbkdf2_iterations;
+       u_int8_t        pbkdf2_salt[LUKS_SALT_LEN];
+       u_int32_t       key_material_offset;    /* in sectors */
+       u_int32_t       stripes;
+};
+
+struct luks_phdr {
+       u_int8_t        magic[LUKS_MAGIC_LEN];
+       u_int16_t       version;
+       char            cipher_name[LUKS_CIPHER_NAME_LEN];
+       char            cipher_mode[LUKS_CIPHER_MODE_LEN];
+       char            hash_spec[LUKS_HASH_SPEC_LEN];
+       u_int32_t       payload_offset;         /* in sectors */
+       u_int32_t       master_key_bytes;
+       u_int8_t        master_key_pbkdf2[LUKS_DIGEST_LEN];
+       u_int8_t        master_key_pbkdf2_salt[LUKS_SALT_LEN];
+       u_int32_t       master_key_pbkdf2_iterations;
+       char            uuid[LUKS_UUID_STRING_LEN];     /* no comment. */
+
+       struct luks_key keyblock[LUKS_NUM_KEYS];
+};
Index: dev/softraid.c
===================================================================
RCS file: /cvs/src/sys/dev/softraid.c,v
retrieving revision 1.353
diff -u -p -r1.353 softraid.c
--- dev/softraid.c      20 May 2015 15:21:57 -0000      1.353
+++ dev/softraid.c      24 May 2015 12:02:30 -0000
@@ -48,6 +48,7 @@
 #include <scsi/scsi_disk.h>
 
 #include <dev/softraidvar.h>
+#include <dev/softraid_luks.h>
 
 #ifdef HIBERNATE
 #include <lib/libsa/aes_xts.h>
@@ -154,8 +155,6 @@ void                        sr_sensors_delete(struct 
sr_disci
 /* metadata */
 int                    sr_meta_probe(struct sr_discipline *, dev_t *, int);
 int                    sr_meta_attach(struct sr_discipline *, int, int);
-int                    sr_meta_rw(struct sr_discipline *, dev_t, void *,
-                           size_t, daddr_t, long);
 int                    sr_meta_clear(struct sr_discipline *);
 void                   sr_meta_init(struct sr_discipline *, int, int);
 void                   sr_meta_init_complete(struct sr_discipline *);
@@ -201,8 +200,8 @@ void                        sr_meta_print(struct 
sr_metadata 
 
 /* the metadata driver should remain stateless */
 struct sr_meta_driver {
-       daddr_t                 smd_offset;     /* metadata location */
-       u_int32_t               smd_size;       /* size of metadata */
+       daddr_t                 smd_offset;     /* metadata location (bytes) */
+       u_int32_t               smd_size;       /* size of metadata (bytes) */
 
        int                     (*smd_probe)(struct sr_softc *,
                                   struct sr_chunk *);
@@ -218,7 +217,10 @@ struct sr_meta_driver {
        { SR_META_OFFSET, SR_META_SIZE * 512,
          sr_meta_native_probe, sr_meta_native_attach, NULL,
          sr_meta_native_read, sr_meta_native_write, NULL },
-       { 0, 0, NULL, NULL, NULL, NULL }
+       { LUKS_META_OFFSET, LUKS_META_SIZE,
+         luks_meta_probe, luks_meta_attach, NULL,
+         luks_meta_read, luks_meta_write, luks_meta_validate },
+       { 0, 0, NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 int
@@ -770,13 +772,7 @@ sr_meta_read(struct sr_discipline *sd)
                        continue;
                }
 
-               if (sm->ssdi.ssd_magic != SR_MAGIC) {
-                       DNPRINTF(SR_D_META, "%s: sr_meta_read !SR_MAGIC\n",
-                           DEVNAME(sc));
-                       continue;
-               }
-
-               /* validate metadata */
+               /* validate metadata and convert to softraid native format */
                if (sr_meta_validate(sd, ch_entry->src_dev_mm, sm, fm)) {
                        DNPRINTF(SR_D_META, "%s: invalid metadata\n",
                            DEVNAME(sc));
@@ -3403,8 +3399,8 @@ sr_ioctl_createraid(struct sr_softc *sc,
 
                /* Ensure metadata level matches requested assembly level. */
                if (sd->sd_meta->ssdi.ssd_level != bc->bc_level) {
-                       sr_error(sc, "volume level does not match metadata "
-                           "level");
+                       sr_error(sc, "volume level %u does not match metadata "
+                           "level %u", sd->sd_meta->ssdi.ssd_level, 
bc->bc_level);
                        goto unwind;
                }
 
@@ -3965,6 +3961,9 @@ sr_discipline_init(struct sr_discipline 
 #ifdef CRYPTO
        case 'C':
                sr_crypto_discipline_init(sd);
+               break;
+       case 'L':
+               sr_luks_discipline_init(sd);
                break;
 #endif
        case 'c':
Index: dev/softraid_crypto.c
===================================================================
RCS file: /cvs/src/sys/dev/softraid_crypto.c,v
retrieving revision 1.117
diff -u -p -r1.117 softraid_crypto.c
--- dev/softraid_crypto.c       14 Mar 2015 03:38:46 -0000      1.117
+++ dev/softraid_crypto.c       24 May 2015 12:02:30 -0000
@@ -1127,7 +1127,7 @@ sr_crypto_write(struct cryptop *crp)
        struct sr_workunit      *wu = &crwu->cr_wu;
        int                     s;
 
-       DNPRINTF(SR_D_INTR, "%s: sr_crypto_write: wu %x xs: %x\n",
+       DNPRINTF(SR_D_INTR, "%s: sr_crypto_write: wu %p xs: %p\n",
            DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs);
 
        if (crp->crp_etype) {
@@ -1206,7 +1206,7 @@ sr_crypto_read(struct cryptop *crp)
        struct sr_workunit      *wu = &crwu->cr_wu;
        int                     s;
 
-       DNPRINTF(SR_D_INTR, "%s: sr_crypto_read: wu %x xs: %x\n",
+       DNPRINTF(SR_D_INTR, "%s: sr_crypto_read: wu %p xs: %p\n",
            DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs);
 
        if (crp->crp_etype)
Index: dev/softraid_luks.c
===================================================================
RCS file: dev/softraid_luks.c
diff -N dev/softraid_luks.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/softraid_luks.c 24 May 2015 12:02:30 -0000
@@ -0,0 +1,776 @@
+/*
+ * Copyright (c) 2015 Martin Pelikan <peli...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This is a bridge between a LUKS implementation and the softraid(4) 
framework.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/queue.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/pool.h>
+#include <sys/atomic.h>
+#include <sys/stdint.h>
+#include <sys/disklabel.h>
+#include <sys/vnode.h>
+#include <sys/disk.h>
+#include <sys/dkio.h>
+#include <sys/uuid.h>
+
+#include <crypto/cryptodev.h>
+
+#include <dev/softraidvar.h>
+#include <dev/softraid_luks.h>
+#include <dev/luks.h>
+
+/* Decrypting and verifying eight keys can be done in parallel.  */
+struct user_key_context {
+       struct task              task;
+       struct sr_discipline    *sd;
+       void                    *kdf;
+       void                    *masterkey;
+       volatile unsigned int   *done;
+       u_int64_t                sessid;
+       struct cryptoini         cri;
+       struct uio               uio;
+       struct iovec             iov;
+       int                      keyidx;
+       int                      error;
+};
+
+extern struct taskq *crypto_taskq;
+
+static int     sr_luks_crypto_init(struct sr_discipline *);
+static void    sr_luks_hotplug(struct sr_discipline *, struct disk *, int);
+
+/* virtual methods of our sr_discipline, all return errno */
+int    sr_luks_alloc_resources(struct sr_discipline *);
+int    sr_luks_assemble(struct sr_discipline *,
+           struct bioc_createraid *, int, void *);
+int    sr_luks_create(struct sr_discipline *,
+           struct bioc_createraid *, int, int64_t);
+void   sr_luks_free_resources(struct sr_discipline *);
+int    sr_luks_rw(struct sr_workunit *);
+void   sr_luks_done(struct sr_workunit *);
+
+
+void
+sr_luks_discipline_init(struct sr_discipline *sd)
+{
+       strlcpy(sd->sd_name, "LUKS", sizeof sd->sd_name);
+       sd->sd_capabilities = SR_CAP_SYSTEM_DISK;
+       sd->sd_max_wu = SR_CRYPTO_NOWU;
+
+       sd->mds.mdd_luks.sl_sessionid = (u_int64_t)-1;
+
+       /* Virtual methods doing the actual work. */
+       sd->sd_alloc_resources = sr_luks_alloc_resources;
+       sd->sd_assemble = sr_luks_assemble;
+       sd->sd_create = sr_luks_create;
+       sd->sd_free_resources = sr_luks_free_resources;
+       sd->sd_scsi_rw = sr_luks_rw;
+       sd->sd_scsi_done = sr_luks_done;
+}
+
+int
+luks_meta_probe(struct sr_softc *sc, struct sr_chunk *ch_entry)
+{
+       struct disklabel         label;
+       int                      error, part = DISKPART(ch_entry->src_dev_mm);
+       daddr_t                  size;
+#ifdef SR_DEBUG
+       char                    *devname = ch_entry->src_devname;
+#endif
+
+       error = VOP_IOCTL(ch_entry->src_vn, DIOCGDINFO, (caddr_t)&label, FREAD,
+           NOCRED, curproc);
+       if (error) {
+               DNPRINTF(SR_D_META, "%s: %s can't obtain disklabel\n",
+                   DEVNAME(sc), devname);
+               goto fail;
+       }
+       memcpy(ch_entry->src_duid, label.d_uid, sizeof ch_entry->src_duid);
+
+       if (label.d_partitions[part].p_fstype != FS_LUKS) {
+               DNPRINTF(SR_D_META, "%s: %s partition not of type LUKS (%d)\n",
+                   DEVNAME(sc), devname, label.d_partitions[part].p_fstype);
+               goto fail;
+       }
+
+       size = DL_SECTOBLK(&label, DL_GETPSIZE(&label.d_partitions[part]));
+       if (size <= LUKS_META_SIZE) {
+               DNPRINTF(SR_D_META, "%s: %s partition too small: %llu blocks\n",
+                   DEVNAME(sc), devname, size);
+               goto fail;
+       }
+       ch_entry->src_size = size;
+
+       DNPRINTF(SR_D_META, "%s: probe found %s size %zu blocks\n", DEVNAME(sc),
+           devname, (size_t) size);
+
+       return (SR_META_F_LUKS);
+ fail:
+       DNPRINTF(SR_D_META, "%s: invalid device: %s\n", DEVNAME(sc),
+           devname ? devname : "no device");
+       return (SR_META_F_INVALID);
+}
+
+int
+luks_meta_attach(struct sr_discipline *sd, int force)
+{
+       struct sr_chunk_head    *cl = &sd->sd_vol.sv_chunk_list;
+       struct sr_chunk         *ch_entry;
+
+       DNPRINTF(SR_D_META, "%s: luks_meta_attach\n", DEVNAME(sd->sd_sc));
+
+       /* Store our chunk size; there should be only one chunk anyway. */
+       sd->sd_vol.sv_chunk_minsz = INT64_MAX;
+       sd->sd_vol.sv_chunk_maxsz = 0;
+
+       SLIST_FOREACH(ch_entry, cl, src_link) {
+               if (ch_entry->src_size < sd->sd_vol.sv_chunk_minsz)
+                       sd->sd_vol.sv_chunk_minsz = ch_entry->src_size;
+               if (ch_entry->src_size > sd->sd_vol.sv_chunk_maxsz)
+                       sd->sd_vol.sv_chunk_maxsz = ch_entry->src_size;
+
+               /* No integrity checking, everything is always online. */
+               ch_entry->src_meta.scm_status = BIOC_SDONLINE;
+       }
+
+       if (sd->sd_vol.sv_chunk_minsz != sd->sd_vol.sv_chunk_maxsz) {
+               sr_error(sd->sd_sc, "too many LUKS chunks\n");
+               return (1);
+       }
+
+       return (0);
+}
+
+int
+luks_meta_read(struct sr_discipline *sd, dev_t dev, struct sr_metadata *md,
+    void *fm)
+{
+       /*
+        * If the caller wants foreign metadata, we must leave @md intact,
+        * because native metadata can contain headers well past sizeof(md)
+        * boundary, which we would fill with our rubbish.
+        */
+       if (fm == NULL) {
+               DNPRINTF(SR_D_META, "didn't ask for foreign metadata!\n");
+               return sr_meta_rw(sd, dev, md,
+                   LUKS_META_SIZE, LUKS_META_OFFSET, B_READ);
+       }
+       return sr_meta_rw(sd, dev, fm,
+           LUKS_META_SIZE, LUKS_META_OFFSET, B_READ);
+}
+
+int
+luks_meta_write(struct sr_discipline *sd, dev_t dev, struct sr_metadata *md,
+    void *fm)
+{
+       if ((sd->sd_meta->ssd_meta_flags & SR_META_DIRTY) == 0)
+               return (0);
+
+       return (EOPNOTSUPP);
+}
+
+/* Whatever we fake in @md will be relied upon and accessed as sd->sd_meta. */
+int
+luks_meta_validate(struct sr_discipline *sd, struct sr_metadata *md, void *fm)
+{
+       struct luks_phdr        *luks;
+       size_t                   chunksz, start;
+       const char              *errstr;
+       int                      error;
+
+       chunksz = sd->sd_vol.sv_chunk_minsz;
+
+       /* Preserve the real LUKS metadata for future reference. */
+       luks = malloc(sizeof *luks, M_DEVBUF, M_NOWAIT);
+       if (luks == NULL) {
+               sr_error(sd->sd_sc, "out of memory for LUKS phdr");
+               return (1);
+       }
+       memcpy(luks, fm, sizeof *luks);
+       sd->mds.mdd_luks.sl_phdr = luks;
+       errstr = luks_phdr_parse_and_validate(&sd->mds.mdd_luks, chunksz);
+       if (errstr != NULL) {
+               sr_error(sd->sd_sc, "invalid LUKS header: %s", errstr);
+               return (1);
+       }
+
+       /* Pretend this is a softraid(4) volume by faking its metadata. */
+       memset(md, 0, sizeof *md);
+       md->ssdi.ssd_magic = SR_MAGIC;
+       md->ssdi.ssd_version = SR_META_VERSION;
+
+       strlcpy(md->ssdi.ssd_vendor, "LUKS", sizeof md->ssdi.ssd_vendor);
+       strlcpy(md->ssdi.ssd_product, luks->cipher_name,
+           sizeof md->ssdi.ssd_product);
+       strlcat(md->ssdi.ssd_product, ",", sizeof md->ssdi.ssd_product);
+       strlcat(md->ssdi.ssd_product, luks->cipher_mode,
+           sizeof md->ssdi.ssd_product);
+
+       error = uuid_from_string(luks->uuid, &md->ssdi.ssd_uuid);
+       if (error) {
+               printf("uuid_from_string %s failed: %d\n", luks->uuid, error);
+               return (1);
+       }
+       md->ssdi.ssd_opt_no = 0;
+       md->ssdi.ssd_level = 'L';
+
+       /* The whole volume is encrypted using just one master key. */
+       sd->sd_max_ccb_per_wu = md->ssdi.ssd_chunk_no = 1;
+
+       /* Compute the size of the device minus physical metadata. */
+       start = betoh32(luks->payload_offset);
+       md->ssdi.ssd_size = chunksz - start;
+       md->ssd_data_offset = start * DEV_BSIZE;
+
+       DNPRINTF(SR_D_META, "%s: available %llu blocks (%zu - %zu)\n",
+           DEVNAME(sd->sd_sc), md->ssdi.ssd_size, start, chunksz);
+
+       /* Fake our own checksum as well. */
+       sr_checksum(sd->sd_sc, md,
+           &md->ssd_checksum, sizeof(struct sr_meta_invariant));
+
+       return (0);
+}
+
+int
+sr_luks_unlock_key_callback(struct cryptop *arg)
+{
+       /* nothing here, but crypto(9) needs something. */
+       return (0);
+}
+
+void
+sr_luks_unlock_key(void *arg)
+{
+       struct user_key_context *ctx = arg;
+       struct sr_discipline    *sd = ctx->sd;
+       struct sr_luks          *sl = &sd->mds.mdd_luks;
+       struct luks_phdr        *luks = sl->sl_phdr;
+       struct cryptop          *crp;
+       struct cryptodesc       *crd;
+       size_t                   bytes, stripes, offset, len;
+       void                    *userkey;
+       u_int64_t                blk;
+       int                      i = ctx->keyidx;
+       dev_t                    dev;
+
+       bytes = sl->sl_keylen / CHAR_BIT;
+
+       KASSERT(luks->keyblock[i].active == htobe32(LUKS_KEY_ACTIVE_MAGIC));
+
+       /* Load the key from disk. */
+       stripes = betoh32(luks->keyblock[i].stripes);
+       offset = betoh32(luks->keyblock[i].key_material_offset);
+       userkey = mallocarray(stripes, bytes, M_DEVBUF, M_WAITOK);
+       len = stripes * bytes;
+
+       DNPRINTF(SR_D_DIS, "%s: sr_luks_unlock: key %d at sector %zu: "
+           "%zu bytes x %zu stripes\n",
+           DEVNAME(sd->sd_sc), i, offset, bytes, stripes);
+
+       dev = sd->sd_vol.sv_chunks[0]->src_dev_mm;
+       ctx->error = sr_meta_rw(sd, dev, userkey, len, offset, B_READ);
+       if (ctx->error) {
+               printf("%s: read(userkey) %d: %d\n", __func__, i, ctx->error);
+               goto done;
+       }
+
+       ctx->cri.cri_alg = sl->sl_alg;
+       ctx->cri.cri_klen = sl->sl_keylen;
+       ctx->cri.cri_key = ctx->kdf;
+
+       ctx->error = crypto_newsession(&ctx->sessid, &ctx->cri, 0);
+       if (ctx->error) {
+               printf("%s: crypto_newsession for key %d: %d\n", __func__, i,
+                   ctx->error);
+               goto done;
+       }
+
+       crp = crypto_getreq(len / DEV_BSIZE);
+       if (crp == NULL) {
+               printf("%s: crypto_getreq for key %d\n", __func__, i);
+               goto freesession;
+       }
+
+       ctx->iov.iov_base = userkey;
+       ctx->iov.iov_len = len;
+
+       ctx->uio.uio_iovcnt = 1;
+       ctx->uio.uio_iov = &ctx->iov;
+
+       crp->crp_buf = &ctx->uio;
+       crp->crp_sid = ctx->sessid;
+       crp->crp_flags = CRYPTO_F_NOQUEUE;
+       crp->crp_ilen = len;
+       crp->crp_alloctype = M_DEVBUF;
+       crp->crp_callback = sr_luks_unlock_key_callback;
+
+       /* Prepare descriptors with per-block IVs and KDF result as input. */
+       for (blk = 0, crd = crp->crp_desc; crd; ++blk, crd = crd->crd_next) {
+               KASSERT(crd != NULL);
+
+               crd->crd_skip = blk << DEV_BSHIFT;
+               crd->crd_len = DEV_BSIZE;
+               crd->crd_inject = 0;
+               crd->crd_flags = CRD_F_IV_PRESENT | CRD_F_IV_EXPLICIT;
+               crd->crd_alg = sl->sl_alg;
+               crd->crd_klen = sl->sl_keylen;
+               crd->crd_key = ctx->kdf;
+               memcpy(crd->crd_iv, &blk, sizeof blk);
+       }
+
+       /* Decrypt the obfuscated master key, */
+       ctx->error = crypto_invoke(crp);
+       if (ctx->error) {
+               printf("%s: crypto_invoke %d: %d\n", __func__, i, ctx->error);
+               goto freecrp;
+       }
+
+       ctx->masterkey = malloc(bytes, M_DEVBUF, M_WAITOK);
+
+       ctx->error = luks_deobfuscate_key(luks, ctx->masterkey, userkey,
+           ctx->kdf, bytes, stripes);
+       if (ctx->error) {
+               /* Bad passphrase, probably. */
+               explicit_bzero(ctx->masterkey, bytes);
+               free(ctx->masterkey, M_DEVBUF, bytes);
+               ctx->masterkey = NULL;
+               goto freecrp;
+       }
+
+ freecrp:
+       crypto_freereq(crp);
+ freesession:
+       crypto_freesession(ctx->sessid);
+ done:
+       explicit_bzero(userkey, len);
+       free(userkey, M_DEVBUF, len);
+
+       atomic_inc_int(ctx->done);
+       wakeup(sd);
+}
+
+int
+sr_luks_unlock(struct sr_discipline *sd, struct bioc_createraid *bc)
+{
+       struct sr_luks          *sl = &sd->mds.mdd_luks;
+       struct luks_phdr        *luks = sl->sl_phdr;
+       struct user_key_context  keys[LUKS_NUM_KEYS];
+       volatile unsigned int    done = 0;
+       size_t                   expected, bytes;
+       int                      i, need, error = EINVAL;
+       u_int8_t                *response;
+
+       bytes = sl->sl_keylen / CHAR_BIT;
+       expected = bytes * LUKS_NUM_KEYS;
+
+       if (!(bc->bc_opaque_flags & BIOC_SOIN) ||
+           bc->bc_opaque_size != expected)
+               return (error);
+
+       response = malloc(expected, M_DEVBUF, M_WAITOK);
+       if ((error = copyin(bc->bc_opaque, response, expected))) {
+               free(response, M_DEVBUF, expected);
+               return (error);
+       }
+
+       memset(&keys, 0, sizeof keys);
+       for (need = 0, i = 0; i < LUKS_NUM_KEYS; ++i) {
+               if (luks->keyblock[i].active != htobe32(LUKS_KEY_ACTIVE_MAGIC))
+                       continue;
+
+               keys[i].sd = sd;
+               keys[i].keyidx = i;
+               keys[i].done = &done;
+               keys[i].kdf = response + (i * bytes);
+
+               ++need;
+               task_set(&keys[i].task, sr_luks_unlock_key, &keys[i]);
+               task_add(crypto_taskq, &keys[i].task);
+       }
+
+       while (done != need) {
+               tsleep(sd, PWAIT, "lukskey", 0);
+       }
+
+       explicit_bzero(response, expected);
+       free(response, M_DEVBUF, expected);
+
+       /* Collect the first succesful master key (there should be only one). */
+       for (i = 0; i < LUKS_NUM_KEYS; ++i) {
+               if (keys[i].masterkey == NULL)
+                       continue;
+
+               if (sl->sl_masterkey == NULL)
+                       sl->sl_masterkey = keys[i].masterkey;
+               else {
+                       explicit_bzero(keys[i].masterkey, bytes);
+                       free(keys[i].masterkey, M_DEVBUF, bytes);
+               }
+       }
+
+       if (sl->sl_masterkey == NULL) {
+               sr_error(sd->sd_sc, "incorrect key or passphrase");
+               return (EPERM);
+       } else {
+               bc->bc_opaque_status = BIOC_SOINOUT_OK;
+               error = sr_luks_crypto_init(sd);
+       }
+
+       return (error); 
+}
+
+int
+sr_luks_assemble(struct sr_discipline *sd, struct bioc_createraid *bc,
+    int no_chunk, void *data)
+{
+       struct luks_phdr *luks = sd->mds.mdd_luks.sl_phdr;
+       int error = EINVAL, k;
+
+       /*
+        * Provide userland with kdf hint, or check its response.
+        */
+       if (bc->bc_opaque_flags & BIOC_SOOUT) {
+               struct luks_user_pbkdf2 hint;
+
+               if (sizeof hint < bc->bc_opaque_size)
+                       return (error);
+
+               hint.key_bytes = sd->mds.mdd_luks.sl_keylen / CHAR_BIT;
+               hint.have_keys = 0;
+
+               for (k = 0; k < LUKS_NUM_KEYS; ++k) {
+                       if (luks->keyblock[k].active !=
+                           htobe32(LUKS_KEY_ACTIVE_MAGIC))
+                               continue;
+
+                       hint.have_keys |= (1 << k);
+                       memcpy(&hint.keys[k].salt,
+                           luks->keyblock[k].pbkdf2_salt, LUKS_SALT_LEN);
+                       hint.keys[k].iterations =
+                           betoh32(luks->keyblock[k].pbkdf2_iterations);
+               }
+
+               if ((error = copyout(&hint, bc->bc_opaque, bc->bc_opaque_size)))
+                       return (error);
+
+               /* We're done, the user has to take action. */
+               bc->bc_opaque_status = BIOC_SOINOUT_OK;
+               error = EAGAIN;
+       }
+       else if (bc->bc_opaque_flags & BIOC_SOIN) {
+               /* Use userland-provided KDF result to obtain the master key. */
+               error = sr_luks_unlock(sd, bc);
+       }
+       else {
+               sr_error(sd->sd_sc, "%s: invalid opaque flags %x\n", __func__,
+                   bc->bc_opaque_flags);
+       }
+
+       return (error);
+}
+
+int
+sr_luks_create(struct sr_discipline *sd, struct bioc_createraid *bc,
+    int no_chunk, int64_t coerced_size)
+{
+       return (EOPNOTSUPP);
+}
+
+static int
+sr_luks_crypto_init(struct sr_discipline *sd)
+{
+       struct sr_luks          *sl = &sd->mds.mdd_luks;
+       struct cryptoini         cri;
+       int                      error;
+
+       memset(&cri, 0, sizeof cri);
+
+       cri.cri_alg = sl->sl_alg;
+       cri.cri_klen = sl->sl_keylen;
+       cri.cri_key = sl->sl_masterkey;
+
+       error = crypto_newsession(&sl->sl_sessionid, &cri, 0 /* no hardware */);
+       if (error) {
+               sr_error(sd->sd_sc, "LUKS crypto_newsession");
+               return (error);
+       }
+
+       return (0);
+}
+
+struct sr_luks_wu *
+sr_luks_crypto_prepare(struct sr_workunit *wu, int encrypt)
+{
+       struct scsi_xfer        *xs = wu->swu_xs;
+       struct sr_discipline    *sd = wu->swu_dis;
+       struct sr_luks_wu       *lwu = (struct sr_luks_wu *)wu;
+       struct cryptodesc       *crd;
+       int                      flags, i, n;
+       daddr_t                  blk;
+
+       lwu->lwu_uio.uio_iovcnt = 1;
+       lwu->lwu_uio.uio_iov[0].iov_len = xs->datalen;
+       if (xs->flags & SCSI_DATA_OUT) {
+               memcpy(lwu->lwu_dmabuf, xs->data, xs->datalen);
+               lwu->lwu_uio.uio_iov[0].iov_base = lwu->lwu_dmabuf;
+       } else
+               lwu->lwu_uio.uio_iov[0].iov_base = xs->data;
+
+       blk = wu->swu_blk_start;
+       n = xs->datalen >> DEV_BSHIFT;
+
+       crd = lwu->lwu_descs;
+       for (i = 0; i < ((MAXPHYS >> DEV_BSHIFT) - n); ++i) {
+               crd = crd->crd_next;
+               KASSERT(crd);
+       }
+       lwu->lwu_crp->crp_desc = crd;
+       flags = (encrypt ? CRD_F_ENCRYPT : 0) |
+           CRD_F_IV_PRESENT | CRD_F_IV_EXPLICIT;
+
+       lwu->lwu_crp->crp_sid = sd->mds.mdd_luks.sl_sessionid;
+       lwu->lwu_crp->crp_opaque = lwu;
+       lwu->lwu_crp->crp_ilen = xs->datalen;
+       lwu->lwu_crp->crp_alloctype = M_DEVBUF;
+       lwu->lwu_crp->crp_buf = &lwu->lwu_uio;
+
+       for (i = 0, crd = lwu->lwu_crp->crp_desc; crd;
+           ++i, ++blk, crd = crd->crd_next) {
+               crd->crd_skip = i << DEV_BSHIFT;
+               crd->crd_len = DEV_BSIZE;
+               crd->crd_inject = 0;
+               crd->crd_flags = flags;
+               crd->crd_alg = sd->mds.mdd_luks.sl_alg;
+               crd->crd_klen = sd->mds.mdd_luks.sl_keylen;
+               crd->crd_key = sd->mds.mdd_luks.sl_masterkey;
+               memcpy(crd->crd_iv, &blk, sizeof blk);
+       }
+
+       return (lwu);
+}
+
+int
+sr_luks_alloc_resources(struct sr_discipline *sd)
+{
+       struct sr_workunit      *wu;
+       struct sr_luks_wu       *lwu;
+
+       if (sr_wu_alloc(sd, sizeof(struct sr_luks_wu))) {
+               sr_error(sd->sd_sc, "unable to allocate work units");
+               return (ENOMEM);
+       }
+       if (sr_ccb_alloc(sd)) {
+               sr_error(sd->sd_sc, "unable to allocate CCBs");
+               return (ENOMEM);
+       }
+
+       /* Connect preallocated uio/iovec/crypto structures like sr_crypto. */
+       TAILQ_FOREACH(wu, &sd->sd_wu, swu_next) {
+               lwu = (struct sr_luks_wu *)wu;
+               lwu->lwu_uio.uio_iov = &lwu->lwu_iov;
+
+               lwu->lwu_dmabuf = dma_alloc(MAXPHYS, PR_WAITOK);
+               lwu->lwu_crp = crypto_getreq(MAXPHYS >> DEV_BSHIFT);
+               if (lwu->lwu_crp == NULL)
+                       return (ENOMEM);
+               lwu->lwu_descs = lwu->lwu_crp->crp_desc;
+       }
+
+       sr_hotplug_register(sd, sr_luks_hotplug);
+
+       return (0);
+}
+
+void
+sr_luks_free_resources(struct sr_discipline *sd)
+{
+       struct sr_luks          *sl = &sd->mds.mdd_luks;
+       struct luks_phdr        *luks = sl->sl_phdr;
+       struct sr_workunit      *wu;
+       struct sr_luks_wu       *lwu;
+       size_t                   keylen;
+
+       DNPRINTF(SR_D_DIS, "%s: %s\n", DEVNAME(sd->sd_sc), __func__);
+
+       /*
+        * Make sure the master key is gone in all cases!
+        */
+
+       /* We might have never made it to luks_meta_validate. */
+       if (luks == NULL) {
+               KASSERT(sl->sl_masterkey == NULL);
+               return;
+       }
+
+       keylen = betoh32(luks->master_key_bytes);
+       if (sl->sl_masterkey != NULL) {
+               explicit_bzero(sl->sl_masterkey, keylen);
+               free(sl->sl_masterkey, M_DEVBUF, keylen);
+               sl->sl_masterkey = NULL;
+       }
+
+       free(sl->sl_phdr, M_DEVBUF, sizeof(struct luks_phdr));
+
+       sr_hotplug_unregister(sd, sr_luks_hotplug);
+
+       /* Free preallocated DMA buffers and crypto descriptors. */
+       TAILQ_FOREACH(wu, &sd->sd_wu, swu_next) {
+               lwu = (struct sr_luks_wu *)wu;
+               if (lwu->lwu_dmabuf)
+                       dma_free(lwu->lwu_dmabuf, MAXPHYS);
+               if (lwu->lwu_crp) {
+                       lwu->lwu_crp->crp_desc = lwu->lwu_descs;
+                       crypto_freereq(lwu->lwu_crp);
+               }
+       }
+
+       crypto_freesession(sl->sl_sessionid);
+       sl->sl_sessionid = (u_int64_t)-1;
+
+       sr_wu_free(sd);
+       sr_ccb_free(sd);
+}
+
+int
+sr_luks_dev_rw(struct sr_workunit *wu)
+{
+       struct sr_luks_wu       *lwu = (struct sr_luks_wu *)wu;
+       struct sr_discipline    *sd = wu->swu_dis;
+       struct scsi_xfer        *xs = wu->swu_xs;
+       struct sr_ccb           *ccb;
+       struct uio              *uio;
+       daddr_t                  blk;
+
+       blk = wu->swu_blk_start + sd->sd_meta->ssd_data_offset / DEV_BSIZE;
+
+       ccb = sr_ccb_rw(sd, 0, blk, xs->datalen, xs->data, xs->flags, 0);
+       if (ccb == NULL) {
+               printf("%s: %s: too many CCBs queued, blk %lld, len %d, "
+                   "flags %x\n", DEVNAME(sd->sd_sc), __func__, blk,
+                   xs->datalen, xs->flags);
+               lwu->lwu_crp->crp_etype = EINVAL;
+               return (1);
+       }
+       if (!ISSET(xs->flags, SCSI_DATA_IN)) {
+               uio = lwu->lwu_crp->crp_buf;
+               ccb->ccb_buf.b_data = uio->uio_iov[0].iov_base;
+               ccb->ccb_opaque = wu;
+       }
+       sr_wu_enqueue_ccb(wu, ccb);
+       sr_schedule_wu(wu);
+
+       return (0);
+}
+
+int
+sr_luks_write(struct cryptop *crp)
+{
+       struct sr_workunit      *wu = crp->crp_opaque;
+       int                      s;
+
+       DNPRINTF(SR_D_INTR, "%s: sr_luks_write: wu: %p xs: %p\n",
+           DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs);
+
+       if (crp->crp_etype) {
+               wu->swu_xs->error = XS_DRIVER_STUFFUP;
+               s = splbio();
+               sr_scsi_done(wu->swu_dis, wu->swu_xs);
+               splx(s);
+       }
+
+       return sr_luks_dev_rw(wu);
+}
+
+int
+sr_luks_rw(struct sr_workunit *wu)
+{
+       struct sr_luks_wu       *lwu = (struct sr_luks_wu *)wu;
+       daddr_t                  blk;
+
+       DNPRINTF(SR_D_INTR, "%s\n", __func__);
+
+       if (sr_validate_io(wu, &blk, "sr_luks_rw")) {
+               DNPRINTF(SR_D_INTR, "invalid I/O blk %lld", blk);
+               return (1);
+       }
+
+       if (wu->swu_xs->flags & SCSI_DATA_OUT) {
+               int error;
+
+               lwu = sr_luks_crypto_prepare(wu, 1);
+               lwu->lwu_crp->crp_callback = sr_luks_write;
+               
+               if ((error = crypto_invoke(lwu->lwu_crp)) == 0)
+                       error = lwu->lwu_crp->crp_etype;
+               return (error);
+       } else
+               return sr_luks_dev_rw(wu);
+}
+
+int
+sr_luks_read(struct cryptop *crp)
+{
+       struct sr_workunit      *wu = crp->crp_opaque;
+       int                      s;
+
+       DNPRINTF(SR_D_INTR, "%s: sr_luks_read: wu: %p xs: %p\n",
+           DEVNAME(wu->swu_dis->sd_sc), wu, wu->swu_xs);
+
+       if (crp->crp_etype)
+               wu->swu_xs->error = XS_DRIVER_STUFFUP;
+
+       s = splbio();
+       sr_scsi_done(wu->swu_dis, wu->swu_xs);
+       splx(s);
+       return (0);
+}
+
+void
+sr_luks_done(struct sr_workunit *wu)
+{
+       struct sr_luks_wu       *lwu;
+       struct scsi_xfer        *xs = wu->swu_xs;
+       int                      s;
+
+       /* Decrypt data succesfully read from the disk. */
+       if (ISSET(xs->flags, SCSI_DATA_IN) && xs->error == XS_NOERROR) {
+               lwu = sr_luks_crypto_prepare(wu, 0 /* decrypt */);
+               lwu->lwu_crp->crp_callback = sr_luks_read;
+               DNPRINTF(SR_D_INTR, "%s: sr_luks_done: crypto_invoke %p\n",
+                   DEVNAME(wu->swu_dis->sd_sc), lwu->lwu_crp);
+               crypto_invoke(lwu->lwu_crp);
+               return;
+       }
+
+       s = splbio();
+       sr_scsi_done(wu->swu_dis, xs);
+       splx(s);
+}
+
+static void
+sr_luks_hotplug(struct sr_discipline *sd, struct disk *diskp, int action)
+{
+       printf("%s: %s %s %d\n", __func__, DEVNAME(sd->sd_sc), diskp->dk_name,
+           action);
+}
Index: dev/softraid_luks.h
===================================================================
RCS file: dev/softraid_luks.h
diff -N dev/softraid_luks.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/softraid_luks.h 24 May 2015 12:02:30 -0000
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 Martin Pelikan <peli...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+#include <dev/luks.h>
+
+/*
+ * Correct userland response is a @key_bytes * LUKS_NUM_KEYS long byte array.
+ */
+struct luks_user_pbkdf2 {
+       u_int32_t       key_bytes;
+       u_int32_t       have_keys;      /* bitmap */
+       struct {
+               u_int32_t       iterations;
+               u_int8_t        salt[LUKS_SALT_LEN];
+       } keys[LUKS_NUM_KEYS];
+};
+
+#if defined(_KERNEL) || defined(_STANDALONE)
+/* Like sr_crypto, embed uio + iovec to avoid risk of allocation failures. */
+struct sr_luks_wu {
+       struct sr_workunit       lwu_workunit;  /* parent class must be first */
+       struct uio               lwu_uio;
+       struct iovec             lwu_iov;
+       struct cryptop          *lwu_crp;
+       struct cryptodesc       *lwu_descs;
+       void                    *lwu_dmabuf;
+};
+
+/* the actual LUKS implementation */
+int            luks_deobfuscate_key(struct luks_phdr *, void *, void *, void *,
+                   size_t, size_t);
+const char *   luks_phdr_parse_and_validate(struct sr_luks *, int64_t);
+
+/* softraid(4) metadata driver */
+int    luks_meta_probe(struct sr_softc *, struct sr_chunk *);
+int    luks_meta_attach(struct sr_discipline *, int);
+int    luks_meta_detach(struct sr_discipline *);
+int    luks_meta_read(struct sr_discipline *, dev_t, struct sr_metadata *,
+           void *);
+int    luks_meta_write(struct sr_discipline *, dev_t, struct sr_metadata *,
+           void *);
+int    luks_meta_validate(struct sr_discipline *, struct sr_metadata *,
+           void *);
+
+#define        LUKS_META_OFFSET        0
+#define        LUKS_META_SIZE          4096    /* variable, depends on header 
within */
+
+/* softraid(4) ioctl(2) integration */
+void   sr_luks_discipline_init(struct sr_discipline *);
+
+/* Mapping between on-disk strings and crypto(9) identifiers. */
+struct luks_cipher {
+       const char      *lc_name;
+       const char      *lc_mode;
+       int              lc_alg;
+       int              lc_klen_min;   /* bits */
+       int              lc_klen_max;   /* bits */
+};
+struct luks_hash {
+       const char      *lh_name;
+       int              lh_alg;
+};
+#endif /* defined(_KERNEL) || defined(_STANDALONE) */
Index: dev/softraidvar.h
===================================================================
RCS file: /cvs/src/sys/dev/softraidvar.h,v
retrieving revision 1.159
diff -u -p -r1.159 softraidvar.h
--- dev/softraidvar.h   27 Jan 2015 03:17:36 -0000      1.159
+++ dev/softraidvar.h   24 May 2015 12:02:30 -0000
@@ -99,6 +99,7 @@ struct sr_crypto_kdfpair {
 #define SR_META_V3_DATA_OFFSET (SR_META_V3_OFFSET + SR_META_V3_SIZE)
 
 #define SR_META_F_NATIVE       0       /* Native metadata format. */
+#define SR_META_F_LUKS         1       /* Linux Unified Key Setup. */
 #define SR_META_F_INVALID      -1
 
 #define SR_HEADER_SIZE         (SR_META_SIZE + SR_BOOT_SIZE)
@@ -153,7 +154,7 @@ struct sr_metadata {
        char                    ssd_devname[32];/* /dev/XXXXX */
        u_int32_t               ssd_meta_flags;
 #define        SR_META_DIRTY           0x1
-       u_int32_t               ssd_data_offset;
+       u_int32_t               ssd_data_offset;/* in bytes */
        u_int64_t               ssd_ondisk;     /* on disk version counter */
        int64_t                 ssd_rebuild;    /* last block of rebuild */
 } __packed;
@@ -457,6 +458,16 @@ struct sr_crypto {
        u_int64_t               scr_sid[SR_CRYPTO_MAXKEYS];
 };
 
+/* LUKS */
+struct sr_luks {
+       void                    *sl_phdr;
+       void                    *sl_masterkey;
+       u_int64_t                sl_sessionid;
+       int                      sl_alg;
+       int                      sl_keylen;     /* bits */
+       int                      sl_hash;
+};
+
 #define SR_CONCAT_NOWU         16
 struct sr_concat {
 };
@@ -521,6 +532,7 @@ struct sr_discipline {
            struct sr_concat    mdd_concat;
 #ifdef CRYPTO
            struct sr_crypto    mdd_crypto;
+           struct sr_luks      mdd_luks;
 #endif /* CRYPTO */
        }                       sd_dis_specific;/* dis specific members */
 #define mds                    sd_dis_specific
@@ -663,6 +675,8 @@ void                        sr_warn(struct sr_softc *, 
const 
 void                   sr_error(struct sr_softc *, const char *, ...);
 int32_t                        sr_validate_stripsize(u_int32_t);
 int                    sr_meta_read(struct sr_discipline *);
+int                    sr_meta_rw(struct sr_discipline *, dev_t, void *,
+                           size_t, daddr_t, long);
 int                    sr_meta_native_read(struct sr_discipline *, dev_t,
                            struct sr_metadata *, void *);
 int                    sr_meta_validate(struct sr_discipline *, dev_t,
Index: kern/kern_uuid.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_uuid.c,v
retrieving revision 1.2
diff -u -p -r1.2 kern_uuid.c
--- kern/kern_uuid.c    31 Aug 2014 20:15:54 -0000      1.2
+++ kern/kern_uuid.c    24 May 2015 12:02:31 -0000
@@ -251,3 +251,58 @@ uuid_dec_be(const void *buf, struct uuid
        for (i = 0; i < _UUID_NODE_LEN; i++)
                uuid->node[i] = p[10 + i];
 }
+
+static inline int
+hex_byte_from_string(const char * __restrict s, u_int8_t * __restrict ret)
+{
+       int i = 0;
+
+       *ret = 0;
+       do {
+               *ret <<= 4;
+
+               if (s[i] >= '0' && s[i] <= '9')
+                       *ret |= s[i] - '0';
+               else if (s[i] >= 'A' && s[i] <= 'F')
+                       *ret |= (s[i] - 'A') + 10;
+               else if (s[i] >= 'a' && s[i] <= 'f')
+                       *ret |= (s[i] - 'a') + 10;
+               else
+                       return 1;
+       } while (++i < 2);
+       return 0;
+}
+
+int
+uuid_from_string(const char * __restrict s, void * __restrict dst)
+{
+       u_int8_t *p = dst;
+       int bytes, shorts;
+
+       /* 32-bit number - (3x 16-bit number -) 48-bit number */
+
+       for (bytes = 0; bytes < 4; ++bytes)
+               if (hex_byte_from_string(s + bytes, p + bytes))
+                       return (1);
+
+       if (s[8] != '-')
+               return (2);
+       s += 9;
+       p += 4;
+
+       for (shorts = 0; shorts < 3; ++shorts) {
+               for (bytes = 0; bytes < 2; ++bytes)
+                       if (hex_byte_from_string(s + bytes, p + bytes))
+                               return (1);
+
+               if (s[4] != '-')
+                       return (2);
+               s += 5;
+               p += 2;
+       }
+
+       for (bytes = 0; bytes < 6; ++bytes)
+               if (hex_byte_from_string(s + bytes, p + bytes))
+                       return (1);
+       return (0);
+}
Index: kern/subr_disk.c
===================================================================
RCS file: /cvs/src/sys/kern/subr_disk.c,v
retrieving revision 1.183
diff -u -p -r1.183 subr_disk.c
--- kern/subr_disk.c    9 May 2015 17:11:26 -0000       1.183
+++ kern/subr_disk.c    24 May 2015 12:02:31 -0000
@@ -418,6 +418,10 @@ donot:
                                fstype = FS_EXT2FS;
                                break;
 
+                       case DOSPTYP_LUKS:
+                               fstype = FS_LUKS;
+                               break;
+
                        case DOSPTYP_NTFS:
                                fstype = FS_NTFS;
                                break;
@@ -567,13 +571,14 @@ gpt_get_fstype(struct uuid *uuid_part)
 {
        static int init = 0;
        static struct uuid uuid_openbsd, uuid_msdos, uuid_chromerootfs,
-           uuid_chromekernelfs, uuid_linux, uuid_hfs, uuid_unused;
+           uuid_chromekernelfs, uuid_linux, uuid_hfs, uuid_luks, uuid_unused;
        static const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
        static const uint8_t gpt_uuid_msdos[] = GPT_UUID_MSDOS;
        static const uint8_t gpt_uuid_chromerootfs[] = GPT_UUID_CHROMEROOTFS;
        static const uint8_t gpt_uuid_chromekernelfs[] = 
GPT_UUID_CHROMEKERNELFS;
        static const uint8_t gpt_uuid_linux[] = GPT_UUID_LINUX;
        static const uint8_t gpt_uuid_hfs[] = GPT_UUID_APPLE_HFS;
+       static const uint8_t gpt_uuid_luks[] = GPT_UUID_LUKS;
        static const uint8_t gpt_uuid_unused[] = GPT_UUID_UNUSED;
 
        if (init == 0) {
@@ -583,6 +588,7 @@ gpt_get_fstype(struct uuid *uuid_part)
                uuid_dec_be(gpt_uuid_chromekernelfs, &uuid_chromekernelfs);
                uuid_dec_be(gpt_uuid_linux, &uuid_linux);
                uuid_dec_be(gpt_uuid_hfs, &uuid_hfs);
+               uuid_dec_be(gpt_uuid_luks, &uuid_luks);
                uuid_dec_be(gpt_uuid_unused, &uuid_unused);
                init = 1;
        }
@@ -601,6 +607,8 @@ gpt_get_fstype(struct uuid *uuid_part)
                return (FS_EXT2FS);
        else if (!memcmp(uuid_part, &uuid_hfs, sizeof(struct uuid)))
                return (FS_HFS);
+       else if (!memcmp(uuid_part, &uuid_luks, sizeof(struct uuid)))
+               return (FS_LUKS);
        else {
                DPRINTF("Unknown GUID: %02x%02x%02x%02x-%02x%02x-%02x%02x-"
                    "%02x%02x-%02x%02x%02x%02x%02x%02x\n",
Index: sys/disklabel.h
===================================================================
RCS file: /cvs/src/sys/sys/disklabel.h,v
retrieving revision 1.65
diff -u -p -r1.65 disklabel.h
--- sys/disklabel.h     9 May 2015 17:11:26 -0000       1.65
+++ sys/disklabel.h     24 May 2015 12:02:31 -0000
@@ -276,6 +276,7 @@ static char *dktypenames[] = {
 #define FS_RAID                19              /* RAIDframe or softraid */
 #define FS_NTFS                20              /* Windows/NT file system */
 #define FS_UDF         21              /* UDF (DVD) filesystem */
+#define FS_LUKS                22              /* Linux Unified Key Setup */
 
 #ifdef DKTYPENAMES
 static char *fstypenames[] = {
@@ -301,6 +302,7 @@ static char *fstypenames[] = {
        "RAID",
        "NTFS",
        "UDF",
+       "LUKS",
        NULL
 };
 
@@ -328,6 +330,7 @@ static char *fstypesnames[] = {
        "",             /* 19 */
        "ntfs",         /* 20 */
        "udf",          /* 21 */
+       "luks",         /* 22 */
        NULL
 };
 
@@ -456,6 +459,9 @@ struct gpt_partition {
 #define GPT_UUID_LINUX_SRV \
     { 0x3b, 0x8f, 0x84, 0x25, 0x20, 0xe0, 0x4f, 0x3b, \
       0x90, 0x7f, 0x1a, 0x25, 0xa7, 0x6f, 0x98, 0xe8 }
+#define GPT_UUID_LUKS \
+    { 0xca, 0x7d, 0x7c, 0xcb, 0x63, 0xed, 0x4c, 0x53, \
+      0x86, 0x1c, 0x17, 0x42, 0x53, 0x60, 0x59, 0xcc }
 #define GPT_UUID_FBSD_DATA \
     { 0x51, 0x6e, 0x7c, 0xb4, 0x6e, 0xcf, 0x11, 0xd6, \
       0x8f, 0xf8, 0x00, 0x02, 0x2d, 0x09, 0x71, 0x2b }
@@ -519,6 +525,7 @@ struct dos_partition {
 #define        DOSPTYP_FREEBSD 0xa5            /* FreeBSD partition type */
 #define        DOSPTYP_OPENBSD 0xa6            /* OpenBSD partition type */
 #define        DOSPTYP_NETBSD  0xa9            /* NetBSD partition type */
+#define        DOSPTYP_LUKS    0xe8            /* Linux Unified Key Setup */
 #define        DOSPTYP_EFI     0xee            /* EFI Protective Partition */
 #define        DOSPTYP_EFISYS  0xef            /* EFI System Partition */
 
Index: sys/uuid.h
===================================================================
RCS file: /cvs/src/sys/sys/uuid.h,v
retrieving revision 1.3
diff -u -p -r1.3 uuid.h
--- sys/uuid.h  31 Aug 2014 09:36:39 -0000      1.3
+++ sys/uuid.h  24 May 2015 12:02:31 -0000
@@ -65,6 +65,7 @@ void  uuid_dec_be(const void *, struct uu
 void   uuid_dec_le(const void *, struct uuid *);
 void   uuid_enc_be(void *, const struct uuid *);
 void   uuid_enc_le(void *, const struct uuid *);
+int    uuid_from_string(const char * __restrict, void * __restrict);
 
 #else  /* _KERNEL */
 

Reply via email to