summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2002-11-04 09:27:01 +0000
committerphk <phk@FreeBSD.org>2002-11-04 09:27:01 +0000
commit16874ad9231c653cc0923d057079cb01dc655144 (patch)
treef63c9d7688c826251a2cc69a8cbf9eace1f02f75 /sys
parent8f58d0f543eb1d2ede1aa0736e9fe5e958cba1d6 (diff)
downloadFreeBSD-src-16874ad9231c653cc0923d057079cb01dc655144.zip
FreeBSD-src-16874ad9231c653cc0923d057079cb01dc655144.tar.gz
Run a revision on the GBDE encryption facility.
Replace ARC4 with SHA2-512. Change lock-structure encoding to use random ordering rather for obscurity. Encrypt lock-structure with AES/256 instead of AES/128. Change kkey derivation to be MD5 hash based. Watch for malloc(M_NOWAIT) failures and ditch our cache when they happen. Remove clause 3 of the license with NAI Labs consent. Many thanks to "Lucky Green" <shamrock@cypherpunks.to> and "David Wagner" <daw@cs.berkeley.edu>, for code reading, inputs and suggestions. This code has still not been stared at for 10 years by a gang of hard-core cryptographers. Discretion advised. NB: These changes result in the on-disk format changing: dump/restore needed. Sponsored by: DARPA & NAI Labs.
Diffstat (limited to 'sys')
-rw-r--r--sys/geom/bde/g_bde.c16
-rw-r--r--sys/geom/bde/g_bde.h95
-rw-r--r--sys/geom/bde/g_bde_crypt.c57
-rw-r--r--sys/geom/bde/g_bde_lock.c454
-rw-r--r--sys/geom/bde/g_bde_work.c51
5 files changed, 429 insertions, 244 deletions
diff --git a/sys/geom/bde/g_bde.c b/sys/geom/bde/g_bde.c
index aaf6323..234c6e6 100644
--- a/sys/geom/bde/g_bde.c
+++ b/sys/geom/bde/g_bde.c
@@ -16,9 +16,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. The names of the authors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -42,12 +39,14 @@
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/malloc.h>
-#include <geom/geom.h>
-#include <geom/bde/g_bde.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
+#include <crypto/rijndael/rijndael.h>
+#include <crypto/sha2/sha2.h>
+#include <geom/geom.h>
+#include <geom/bde/g_bde.h>
#define BDE_CLASS_NAME "BDE"
static void
@@ -176,6 +175,7 @@ g_bde_create(struct g_createargs *ga)
g_topology_unlock();
while (sc->dead != 2 && !LIST_EMPTY(&pp->consumers))
tsleep(sc, PRIBIO, "g_bdedie", hz);
+ g_waitidle();
g_topology_lock();
g_destroy_provider(pp);
mtx_destroy(&sc->worklist_mutex);
@@ -208,6 +208,7 @@ g_bde_create(struct g_createargs *ga)
return (error);
}
g_topology_unlock();
+ g_waitidle();
while (1) {
sectorsize = cp->provider->sectorsize;
mediasize = cp->provider->mediasize;
@@ -217,8 +218,9 @@ g_bde_create(struct g_createargs *ga)
sc->consumer = cp;
error = g_bde_decrypt_lock(sc, ga->ptr,
- (u_char *)ga->ptr + 256, mediasize, sectorsize, NULL);
- bzero(sc->arc4_sbox, sizeof sc->arc4_sbox);
+ (u_char *)ga->ptr + (sizeof sc->sha2),
+ mediasize, sectorsize, NULL);
+ bzero(sc->sha2, sizeof sc->sha2);
if (error)
break;
kp = &sc->key;
diff --git a/sys/geom/bde/g_bde.h b/sys/geom/bde/g_bde.h
index 6354411..c5f9bf5 100644
--- a/sys/geom/bde/g_bde.h
+++ b/sys/geom/bde/g_bde.h
@@ -16,9 +16,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. The names of the authors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -35,7 +32,17 @@
* $FreeBSD$
*/
-/* These are quite, but not entirely unlike constants. */
+#ifndef _SYS_GEOM_BDE_G_BDE_H_
+#define _SYS_GEOM_BDE_G_BDE_H_ 1
+
+/*
+ * These are quite, but not entirely unlike constants.
+ *
+ * They are not commented in details here, to prevent unadvisable
+ * experimentation. Please consult the code where they are used before you
+ * even think about modifying these.
+ */
+
#define G_BDE_MKEYLEN (2048/8)
#define G_BDE_SKEYBITS 128
#define G_BDE_SKEYLEN (G_BDE_SKEYBITS/8)
@@ -43,6 +50,8 @@
#define G_BDE_KKEYLEN (G_BDE_KKEYBITS/8)
#define G_BDE_MAXKEYS 4
#define G_BDE_LOCKSIZE 384
+#define NLOCK_FIELDS 13
+
/* This just needs to be "large enough" */
#define G_BDE_KEYBYTES 304
@@ -62,6 +71,7 @@ struct g_bde_sector {
u_char malloc;
enum {JUNK, IO, VALID} state;
int error;
+ time_t used;
};
struct g_bde_work {
@@ -81,21 +91,31 @@ struct g_bde_work {
int error;
};
+/*
+ * The decrypted contents of the lock sectors. Notice that this is not
+ * the same as the on-disk layout. The on-disk layout is dynamic and
+ * dependent on the pass-phrase.
+ */
struct g_bde_key {
uint64_t sector0;
- /* Physical byte offset of first byte used */
+ /* Physical byte offset of 1st byte used */
uint64_t sectorN;
- /* Physical byte offset of first byte not used */
+ /* Physical byte offset of 1st byte not used */
uint64_t keyoffset;
+ /* Number of bytes the disk image is skewed. */
uint64_t lsector[G_BDE_MAXKEYS];
- /* Physical offsets */
+ /* Physical byte offsets of lock sectors */
uint32_t sectorsize;
+ /* Our "logical" sector size */
uint32_t flags;
/* 1 = lockfile in sector 0 */
- uint8_t hash[16];
uint8_t salt[16];
+ /* Used to frustate the kkey generation */
uint8_t spare[32];
+ /* For future use, random contents */
uint8_t mkey[G_BDE_MKEYLEN];
+ /* Our masterkey. */
+
/* Non-stored help-fields */
uint64_t zone_width; /* On-disk width of zone */
uint64_t zone_cont; /* Payload width of zone */
@@ -114,12 +134,11 @@ struct g_bde_softc {
struct mtx worklist_mutex;
struct proc *thread;
struct g_bde_key key;
- u_char arc4_sbox[256];
- u_char arc4_i, arc4_j;
int dead;
u_int nwork;
u_int nsect;
u_int ncache;
+ u_char sha2[SHA512_DIGEST_LENGTH];
};
/* g_bde_crypt.c */
@@ -133,14 +152,12 @@ int g_bde_get_key(struct g_bde_softc *sc, void *ptr, int len);
int g_bde_init_keybytes(struct g_bde_softc *sc, char *passp, int len);
/* g_bde_lock .c */
-void g_bde_encode_lock(struct g_bde_key *gl, u_char *ptr);
-void g_bde_decode_lock(struct g_bde_key *gl, u_char *ptr);
-u_char g_bde_arc4(struct g_bde_softc *sc);
-void g_bde_arc4_seq(struct g_bde_softc *sc, void *ptr, u_int len);
-void g_bde_arc4_seed(struct g_bde_softc *sc, const void *ptr, u_int len);
-int g_bde_keyloc_encrypt(struct g_bde_softc *sc, void *input, void *output);
-int g_bde_keyloc_decrypt(struct g_bde_softc *sc, void *input, void *output);
-int g_bde_decrypt_lock(struct g_bde_softc *sc, u_char *sbox, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey);
+int g_bde_encode_lock(struct g_bde_softc *sc, struct g_bde_key *gl, u_char *ptr);
+int g_bde_decode_lock(struct g_bde_softc *sc, struct g_bde_key *gl, u_char *ptr);
+int g_bde_keyloc_encrypt(struct g_bde_softc *sc, uint64_t *input, void *output);
+int g_bde_keyloc_decrypt(struct g_bde_softc *sc, void *input, uint64_t *output);
+int g_bde_decrypt_lock(struct g_bde_softc *sc, u_char *keymat, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey);
+void g_bde_hash_pass(struct g_bde_softc *sc, const void *input, u_int len);
/* g_bde_math .c */
uint64_t g_bde_max_sector(struct g_bde_key *lp);
@@ -150,3 +167,45 @@ void g_bde_map_sector(struct g_bde_key *lp, uint64_t isector, uint64_t *osector,
void g_bde_start1(struct bio *bp);
void g_bde_worker(void *arg);
+/*
+ * These four functions wrap the raw Rijndael functions and make sure we
+ * explode if something fails which shouldn't.
+ */
+
+static __inline void
+AES_init(cipherInstance *ci)
+{
+ int error;
+
+ error = rijndael_cipherInit(ci, MODE_CBC, NULL);
+ KASSERT(error > 0, ("rijndael_cipherInit %d", error));
+}
+
+static __inline void
+AES_makekey(keyInstance *ki, int dir, u_int len, void *key)
+{
+ int error;
+
+ error = rijndael_makeKey(ki, dir, len, key);
+ KASSERT(error > 0, ("rijndael_makeKey %d", error));
+}
+
+static __inline void
+AES_encrypt(cipherInstance *ci, keyInstance *ki, void *in, void *out, u_int len)
+{
+ int error;
+
+ error = rijndael_blockEncrypt(ci, ki, in, len * 8, out);
+ KASSERT(error > 0, ("rijndael_blockEncrypt %d", error));
+}
+
+static __inline void
+AES_decrypt(cipherInstance *ci, keyInstance *ki, void *in, void *out, u_int len)
+{
+ int error;
+
+ error = rijndael_blockDecrypt(ci, ki, in, len * 8, out);
+ KASSERT(error > 0, ("rijndael_blockDecrypt %d", error));
+}
+
+#endif /* _SYS_GEOM_BDE_G_BDE_H_ */
diff --git a/sys/geom/bde/g_bde_crypt.c b/sys/geom/bde/g_bde_crypt.c
index 6cabb52..6c545a8 100644
--- a/sys/geom/bde/g_bde_crypt.c
+++ b/sys/geom/bde/g_bde_crypt.c
@@ -16,9 +16,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. The names of the authors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -49,51 +46,12 @@
#include <sys/libkern.h>
#include <sys/md5.h>
-#include <geom/geom.h>
-#include <geom/bde/g_bde.h>
-
#include <crypto/rijndael/rijndael.h>
+#include <crypto/sha2/sha2.h>
-/*
- * These four functions wrap the raw Rijndael functions and make sure we
- * explode if something fails which shouldn't.
- */
-
-static void
-AES_init(cipherInstance *ci)
-{
- int error;
-
- error = rijndael_cipherInit(ci, MODE_CBC, NULL);
- KASSERT(error > 0, ("rijndael_cipherInit %d", error));
-}
-
-static void
-AES_makekey(keyInstance *ki, int dir, u_int len, void *key)
-{
- int error;
-
- error = rijndael_makeKey(ki, dir, len, key);
- KASSERT(error > 0, ("rijndael_makeKey %d", error));
-}
-
-static void
-AES_encrypt(cipherInstance *ci, keyInstance *ki, void *in, void *out, u_int len)
-{
- int error;
-
- error = rijndael_blockEncrypt(ci, ki, in, len * 8, out);
- KASSERT(error > 0, ("rijndael_blockEncrypt %d", error));
-}
-
-static void
-AES_decrypt(cipherInstance *ci, keyInstance *ki, void *in, void *out, u_int len)
-{
- int error;
+#include <geom/geom.h>
+#include <geom/bde/g_bde.h>
- error = rijndael_blockDecrypt(ci, ki, in, len * 8, out);
- KASSERT(error > 0, ("rijndael_blockDecrypt %d", error));
-}
/*
* Derive kkey from mkey + sector offset.
@@ -120,10 +78,14 @@ g_bde_kkey(struct g_bde_softc *sc, keyInstance *ki, int dir, off_t sector)
u_int t;
MD5_CTX ct;
u_char buf[16];
+ u_char buf2[8];
+
+ /* We have to be architecture neutral */
+ g_enc_le8(buf2, sector);
MD5Init(&ct);
MD5Update(&ct, sc->key.salt, 8);
- MD5Update(&ct, (void *)&sector, sizeof sector);
+ MD5Update(&ct, buf2, sizeof buf2);
MD5Update(&ct, sc->key.salt + 8, 8);
MD5Final(buf, &ct);
@@ -131,8 +93,9 @@ g_bde_kkey(struct g_bde_softc *sc, keyInstance *ki, int dir, off_t sector)
for (t = 0; t < 16; t++) {
MD5Update(&ct, &sc->key.mkey[buf[t]], 1);
if (t == 8)
- MD5Update(&ct, (void *)&sector, sizeof sector);
+ MD5Update(&ct, buf2, sizeof buf2);
}
+ bzero(buf2, sizeof buf2);
MD5Final(buf, &ct);
bzero(&ct, sizeof ct);
AES_makekey(ki, dir, G_BDE_KKEYBITS, buf);
diff --git a/sys/geom/bde/g_bde_lock.c b/sys/geom/bde/g_bde_lock.c
index 29c880c..d415c30 100644
--- a/sys/geom/bde/g_bde_lock.c
+++ b/sys/geom/bde/g_bde_lock.c
@@ -16,9 +16,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. The names of the authors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -50,6 +47,9 @@
#include <sys/malloc.h>
#include <sys/systm.h>
#else
+#include <err.h>
+#define CTASSERT(foo)
+#define KASSERT(foo, bar) do { if(!(foo)) { warn bar ; exit (1); } } while (0)
#include <errno.h>
#include <string.h>
#include <stdlib.h>
@@ -57,203 +57,332 @@
#define g_free(foo) free(foo)
#endif
+#include <crypto/rijndael/rijndael.h>
+#include <crypto/sha2/sha2.h>
+
#include <geom/geom.h>
#include <geom/bde/g_bde.h>
-#include <crypto/rijndael/rijndael.h>
-
/*
- * Encode/Decode the lock structure in byte-sequence format.
- *
- * Security objectives: none.
+ * Hash the raw pass-phrase.
*
- * C-structure packing and byte-endianess depends on architecture, compiler
- * and compiler options. We therefore explicitly encode and decode struct
- * g_bde_key using an invariant byte-sequence format.
+ * Security objectives: produce from the pass-phrase a fixed length
+ * bytesequence with PRN like properties in a reproducible way retaining
+ * as much entropy from the pass-phrase as possible.
*
+ * SHA2-512 makes this easy.
*/
void
-g_bde_encode_lock(struct g_bde_key *gl, u_char *ptr)
+g_bde_hash_pass(struct g_bde_softc *sc, const void *input, u_int len)
{
+ SHA512_CTX cx;
- bcopy(gl->hash, ptr + 0, sizeof gl->hash);
- g_enc_le8(ptr + 16, gl->sector0);
- g_enc_le8(ptr + 24, gl->sectorN);
- g_enc_le8(ptr + 32, gl->keyoffset);
- g_enc_le4(ptr + 40, gl->sectorsize);
- g_enc_le4(ptr + 44, gl->flags);
- g_enc_le8(ptr + 48, gl->lsector[0]);
- g_enc_le8(ptr + 56, gl->lsector[1]);
- g_enc_le8(ptr + 64, gl->lsector[2]);
- g_enc_le8(ptr + 72, gl->lsector[3]);
- bcopy(gl->spare, ptr + 80, sizeof gl->spare);
- bcopy(gl->salt, ptr + 112, sizeof gl->salt);
- bcopy(gl->mkey, ptr + 128, sizeof gl->mkey);
-}
-
-void
-g_bde_decode_lock(struct g_bde_key *gl, u_char *ptr)
-{
- bcopy(ptr + 0, gl->hash, sizeof gl->hash);
- gl->sector0 = g_dec_le8(ptr + 16);
- gl->sectorN = g_dec_le8(ptr + 24);
- gl->keyoffset = g_dec_le8(ptr + 32);
- gl->sectorsize = g_dec_le4(ptr + 40);
- gl->flags = g_dec_le4(ptr + 44);
- gl->lsector[0] = g_dec_le8(ptr + 48);
- gl->lsector[1] = g_dec_le8(ptr + 56);
- gl->lsector[2] = g_dec_le8(ptr + 64);
- gl->lsector[3] = g_dec_le8(ptr + 72);
- bcopy(ptr + 80, gl->spare, sizeof gl->spare);
- bcopy(ptr + 112, gl->salt, sizeof gl->salt);
- bcopy(ptr + 128, gl->mkey, sizeof gl->mkey);
+ SHA512_Init(&cx);
+ SHA512_Update(&cx, input, len);
+ SHA512_Final(sc->sha2, &cx);
}
/*
- * Generate key-material used for protecting lock sectors.
+ * Encode/Decode the lock structure in byte-sequence format.
*
- * Security objectives: from the pass-phrase provide by the user, produce a
- * reproducible stream of bits/bytes which resemeble pseudo-random bits.
+ * Security objectives: Store in pass-phrase dependent variant format.
*
- * This is the stream-cipher algorithm called ARC4. See for instance the
- * description in "Applied Cryptography" by Bruce Scneier.
+ * C-structure packing and byte-endianess depends on architecture, compiler
+ * and compiler options. Writing raw structures to disk is therefore a bad
+ * idea in these enlightend days.
+ *
+ * We spend a fraction of the key-material on shuffling the fields around
+ * so they will be stored in an unpredictable sequence.
+ *
+ * For each byte of the key-material we derive two field indexes, and swap
+ * the position of those two fields.
+ *
+ * I have not worked out the statistical properties of this shuffle, but
+ * given that the key-material has PRN properties, the primary objective
+ * of making it hard to figure out which bits are where in the lock sector
+ * is sufficiently fulfilled.
+ *
+ * We include (and shuffle) an extra hash field in the stored version for
+ * identification and versioning purposes. This field contains the MD5 hash
+ * of a version identifier (currently "0000") followed by the stored lock
+ * sector byte-sequence substituting zero bytes for the hash field.
+ *
+ * The stored keysequence is protected by AES/256/CBC elsewhere in the code
+ * so the fact that the generated byte sequence has a much higher than
+ * average density of zero bits (from the numeric fields) is not currently
+ * a concern.
+ *
+ * Should this later become a concern, a simple software update and
+ * pass-phrase change can remedy the situation. One possible solution
+ * could be to XOR the numeric fields with a key-material derived PRN.
+ *
+ * The chosen shuffle algorithm only works as long as we have no more than 16
+ * fields in the stored part of the lock structure (hence the CTASSERT below).
*/
-u_char
-g_bde_arc4(struct g_bde_softc *sc)
+CTASSERT(NLOCK_FIELDS <= 16);
+
+static void
+g_bde_shuffle_lock(struct g_bde_softc *sc, int *buf)
{
- u_char c;
-
- sc->arc4_j += sc->arc4_sbox[++sc->arc4_i];
- c = sc->arc4_sbox[sc->arc4_i];
- sc->arc4_sbox[sc->arc4_i] = sc->arc4_sbox[sc->arc4_j];
- sc->arc4_sbox[sc->arc4_j] = c;
- c = sc->arc4_sbox[sc->arc4_i] + sc->arc4_sbox[sc->arc4_j];
- c = sc->arc4_sbox[c];
- return (c);
+ int i, j, k, l;
+
+ /* Assign the fields sequential positions */
+ for(i = 0; i < NLOCK_FIELDS; i++)
+ buf[i] = i;
+
+ /* Then mix it all up */
+ for(i = 48; i < sizeof(sc->sha2); i++) {
+ j = sc->sha2[i] % NLOCK_FIELDS;
+ k = (sc->sha2[i] / NLOCK_FIELDS) % NLOCK_FIELDS;
+ l = buf[j];
+ buf[j] = buf[k];
+ buf[k] = l;
+ }
}
-void
-g_bde_arc4_seq(struct g_bde_softc *sc, void *ptr, u_int len)
+int
+g_bde_encode_lock(struct g_bde_softc *sc, struct g_bde_key *gl, u_char *ptr)
{
- u_char *p;
-
+ int shuffle[NLOCK_FIELDS];
+ u_char *hash, *p;
+ int i;
+ MD5_CTX c;
+
p = ptr;
- while (len--)
- *p++ = g_bde_arc4(sc);
+ hash = NULL;
+ g_bde_shuffle_lock(sc, shuffle);
+ for (i = 0; i < NLOCK_FIELDS; i++) {
+ switch(shuffle[i]) {
+ case 0:
+ g_enc_le8(p, gl->sector0);
+ p += 8;
+ break;
+ case 1:
+ g_enc_le8(p, gl->sectorN);
+ p += 8;
+ break;
+ case 2:
+ g_enc_le8(p, gl->keyoffset);
+ p += 8;
+ break;
+ case 3:
+ g_enc_le4(p, gl->sectorsize);
+ p += 4;
+ break;
+ case 4:
+ g_enc_le4(p, gl->flags);
+ p += 4;
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ g_enc_le8(p, gl->lsector[shuffle[i] - 5]);
+ p += 8;
+ break;
+ case 9:
+ bcopy(gl->spare, p, sizeof gl->spare);
+ p += sizeof gl->spare;
+ break;
+ case 10:
+ bcopy(gl->salt, p, sizeof gl->salt);
+ p += sizeof gl->salt;
+ break;
+ case 11:
+ bcopy(gl->mkey, p, sizeof gl->mkey);
+ p += sizeof gl->mkey;
+ break;
+ case 12:
+ bzero(p, 16);
+ hash = p;
+ p += 16;
+ break;
+ }
+ }
+ if(ptr + G_BDE_LOCKSIZE != p)
+ return(-1);
+ if (hash == NULL)
+ return(-1);
+ MD5Init(&c);
+ MD5Update(&c, "0000", 4); /* Versioning */
+ MD5Update(&c, ptr, G_BDE_LOCKSIZE);
+ MD5Final(hash, &c);
+ return(0);
}
-void
-g_bde_arc4_seed(struct g_bde_softc *sc, const void *ptr, u_int len)
+int
+g_bde_decode_lock(struct g_bde_softc *sc, struct g_bde_key *gl, u_char *ptr)
{
- u_char k[256], c;
- const u_char *p;
- u_int i;
+ int shuffle[NLOCK_FIELDS];
+ u_char *p;
+ u_char hash[16], hash2[16];
+ MD5_CTX c;
+ int i;
p = ptr;
- sc->arc4_i = 0;
- bzero(k, sizeof k);
- while(len--)
- k[sc->arc4_i++] ^= *p++;
-
- sc->arc4_j = 0;
- for (i = 0; i < 256; i++)
- sc->arc4_sbox[i] = i;
- for (i = 0; i < 256; i++) {
- sc->arc4_j += sc->arc4_sbox[i] + k[i];
- c = sc->arc4_sbox[i];
- sc->arc4_sbox[i] = sc->arc4_sbox[sc->arc4_j];
- sc->arc4_sbox[sc->arc4_j] = c;
+ g_bde_shuffle_lock(sc, shuffle);
+ for (i = 0; i < NLOCK_FIELDS; i++) {
+ switch(shuffle[i]) {
+ case 0:
+ gl->sector0 = g_dec_le8(p);
+ p += 8;
+ break;
+ case 1:
+ gl->sectorN = g_dec_le8(p);
+ p += 8;
+ break;
+ case 2:
+ gl->keyoffset = g_dec_le8(p);
+ p += 8;
+ break;
+ case 3:
+ gl->sectorsize = g_dec_le4(p);
+ p += 4;
+ break;
+ case 4:
+ gl->flags = g_dec_le4(p);
+ p += 4;
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ gl->lsector[shuffle[i] - 5] = g_dec_le8(p);
+ p += 8;
+ break;
+ case 9:
+ bcopy(p, gl->spare, sizeof gl->spare);
+ p += sizeof gl->spare;
+ break;
+ case 10:
+ bcopy(p, gl->salt, sizeof gl->salt);
+ p += sizeof gl->salt;
+ break;
+ case 11:
+ bcopy(p, gl->mkey, sizeof gl->mkey);
+ p += sizeof gl->mkey;
+ break;
+ case 12:
+ bcopy(p, hash2, sizeof hash2);
+ bzero(p, sizeof hash2);
+ p += sizeof hash2;
+ break;
+ }
}
- sc->arc4_i = 0;
- sc->arc4_j = 0;
+ if(ptr + G_BDE_LOCKSIZE != p)
+ return(-1);
+ MD5Init(&c);
+ MD5Update(&c, "0000", 4); /* Versioning */
+ MD5Update(&c, ptr, G_BDE_LOCKSIZE);
+ MD5Final(hash, &c);
+ if (bcmp(hash, hash2, sizeof hash2))
+ return (1);
+ return (0);
}
/*
- * Encrypt/Decrypt the metadata address with key-material.
+ * Encode/Decode the locksector address ("metadata") with key-material.
+ *
+ * Security objectives: Encode/Decode the metadata encrypted by key-material.
+ *
+ * A simple AES/128/CBC will do. We take care to always store the metadata
+ * in the same endianess to make it MI.
+ *
+ * In the typical case the metadata is stored in encrypted format in sector
+ * zero on the media, but at the users discretion or if the piece of the
+ * device used (sector0...sectorN) does not contain sector zero, it can
+ * be stored in a filesystem or on a PostIt.
+ *
+ * The inability to easily locate the lock sectors makes an attack on a
+ * cold disk much less attractive, without unduly inconveniencing the
+ * legitimate user who can feasibly do a brute-force scan if the metadata
+ * was lost.
*/
int
-g_bde_keyloc_encrypt(struct g_bde_softc *sc, void *input, void *output)
+g_bde_keyloc_encrypt(struct g_bde_softc *sc, uint64_t *input, void *output)
{
- u_char *p;
- u_char buf[16], buf1[16];
- u_int i;
+ u_char buf[16];
keyInstance ki;
cipherInstance ci;
- bcopy(input, output, 16);
- return 0;
- rijndael_cipherInit(&ci, MODE_CBC, NULL);
- p = input;
- g_bde_arc4_seq(sc, buf, sizeof buf);
- for (i = 0; i < sizeof buf; i++)
- buf1[i] = p[i] ^ buf[i];
- g_bde_arc4_seq(sc, buf, sizeof buf);
- rijndael_makeKey(&ki, DIR_ENCRYPT, G_BDE_KKEYBITS, buf);
- rijndael_blockEncrypt(&ci, &ki, buf1, 16 * 8, output);
+ g_enc_le8(buf, input[0]);
+ g_enc_le8(buf + 8, input[1]);
+ AES_init(&ci);
+ AES_makekey(&ki, DIR_ENCRYPT, G_BDE_KKEYBITS, sc->sha2 + 0);
+ AES_encrypt(&ci, &ki, buf, output, sizeof buf);
+ bzero(buf, sizeof buf);
bzero(&ci, sizeof ci);
bzero(&ki, sizeof ki);
return (0);
}
int
-g_bde_keyloc_decrypt(struct g_bde_softc *sc, void *input, void *output)
+g_bde_keyloc_decrypt(struct g_bde_softc *sc, void *input, uint64_t *output)
{
- u_char *p;
- u_char buf1[16], buf2[16];
- u_int i;
keyInstance ki;
cipherInstance ci;
-
- bcopy(input, output, 16);
- return 0;
- rijndael_cipherInit(&ci, MODE_CBC, NULL);
- g_bde_arc4_seq(sc, buf1, sizeof buf1);
- g_bde_arc4_seq(sc, buf2, sizeof buf2);
- rijndael_makeKey(&ki, DIR_DECRYPT, G_BDE_KKEYBITS, buf2);
- rijndael_blockDecrypt(&ci, &ki, input, 16 * 8, output);
- p = output;
- for (i = 0; i < sizeof buf1; i++)
- p[i] ^= buf1[i];
+ u_char buf[16];
+
+ AES_init(&ci);
+ AES_makekey(&ki, DIR_DECRYPT, G_BDE_KKEYBITS, sc->sha2 + 0);
+ AES_decrypt(&ci, &ki, input, buf, sizeof buf);
+ output[0] = g_dec_le8(buf);
+ output[1] = g_dec_le8(buf + 8);
+ bzero(buf, sizeof buf);
bzero(&ci, sizeof ci);
bzero(&ki, sizeof ki);
return (0);
}
/*
- * Encode/Decode lock sectors, do the real work.
+ * Find and Encode/Decode lock sectors.
+ *
+ * Security objective: given the pass-phrase, find, decrypt, decode and
+ * validate the lock sector contents.
+ *
+ * For ondisk metadata we cannot know beforehand which of the lock sectors
+ * a given pass-phrase opens so we must try each of the metadata copies in
+ * sector zero in turn. If metadata was passed as an argument, we don't
+ * have this problem.
+ *
*/
static int
-g_bde_decrypt_lockx(struct g_bde_softc *sc, u_char *sbox, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey)
+g_bde_decrypt_lockx(struct g_bde_softc *sc, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey)
{
- u_char *buf, k1buf[16], k2buf[G_BDE_LOCKSIZE], k3buf[16], *q;
+ u_char *buf, *q;
struct g_bde_key *gl;
uint64_t off[2];
int error, m, i;
- MD5_CTX c;
keyInstance ki;
cipherInstance ci;
- rijndael_cipherInit(&ci, MODE_CBC, NULL);
- bcopy(sbox, sc->arc4_sbox, 256);
- sc->arc4_i = 0;
- sc->arc4_j = 0;
gl = &sc->key;
+
+ /* Try to decrypt the metadata */
error = g_bde_keyloc_decrypt(sc, meta, off);
if (error)
return(error);
+ /* loose the random part */
+ off[1] = 0;
+
+ /* If it points ito thin blue air, forget it */
if (off[0] + G_BDE_LOCKSIZE > (uint64_t)mediasize) {
- bzero(off, sizeof off);
+ off[0] = 0;
return (EINVAL);
}
- off[1] = 0;
+
+ /* The lock data may span two physical sectors. */
+
m = 1;
if (off[0] % sectorsize > sectorsize - G_BDE_LOCKSIZE)
m++;
+
+ /* Read the suspected sector(s) */
buf = g_read_data(sc->consumer,
off[0] - (off[0] % sectorsize),
m * sectorsize, &error);
@@ -262,92 +391,83 @@ g_bde_decrypt_lockx(struct g_bde_softc *sc, u_char *sbox, u_char *meta, off_t me
return(error);
}
+ /* Find the byte-offset of the stored byte sequence */
q = buf + off[0] % sectorsize;
- off[1] = 0;
+ /* If it is all zero, somebody nuked our lock sector */
for (i = 0; i < G_BDE_LOCKSIZE; i++)
off[1] += q[i];
-
if (off[1] == 0) {
off[0] = 0;
g_free(buf);
return (ESRCH);
}
- g_bde_arc4_seq(sc, k1buf, sizeof k1buf);
- g_bde_arc4_seq(sc, k2buf, sizeof k2buf);
- g_bde_arc4_seq(sc, k3buf, sizeof k3buf);
-
- MD5Init(&c);
- MD5Update(&c, "0000", 4); /* XXX: for future versioning */
- MD5Update(&c, k1buf, 16);
- MD5Final(k1buf, &c);
-
- rijndael_makeKey(&ki, DIR_DECRYPT, 128, k3buf);
- bzero(k3buf, sizeof k3buf);
- rijndael_blockDecrypt(&ci, &ki, q, G_BDE_LOCKSIZE * 8, q);
-
- for (i = 0; i < G_BDE_LOCKSIZE; i++)
- q[i] ^= k2buf[i];
- bzero(k2buf, sizeof k2buf);
-
- if (bcmp(q, k1buf, sizeof k1buf)) {
- bzero(k1buf, sizeof k1buf);
- bzero(buf, sectorsize * m);
- g_free(buf);
+ /* Decrypt the byte-sequence in place */
+ AES_init(&ci);
+ AES_makekey(&ki, DIR_DECRYPT, 256, sc->sha2 + 16);
+ AES_decrypt(&ci, &ki, q, q, G_BDE_LOCKSIZE);
+
+ /* Decode the byte-sequence */
+ i = g_bde_decode_lock(sc, gl, q);
+ q = NULL;
+ if (i < 0) {
off[0] = 0;
- return (ENOTDIR);
+ return (EDOOFUS); /* Programming error */
+ } else if (i > 0) {
+ off[0] = 0;
+ return (ENOTDIR); /* Hash didn't match */
}
- bzero(k1buf, sizeof k1buf);
- g_bde_decode_lock(gl, q);
bzero(buf, sectorsize * m);
g_free(buf);
+ /* If the masterkey is all zeros, user destroyed it */
off[1] = 0;
for (i = 0; i < (int)sizeof(gl->mkey); i++)
off[1] += gl->mkey[i];
-
- if (off[1] == 0) {
- off[0] = 0;
+ if (off[1] == 0)
return (ENOENT);
- }
+
+ /* Finally, find out which key was used by matching the byte offset */
for (i = 0; i < G_BDE_MAXKEYS; i++)
if (nkey != NULL && off[0] == gl->lsector[i])
*nkey = i;
-
+ off[0] = 0;
return (0);
}
-/*
- * Encode/Decode lock sectors.
- */
-
int
-g_bde_decrypt_lock(struct g_bde_softc *sc, u_char *sbox, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey)
+g_bde_decrypt_lock(struct g_bde_softc *sc, u_char *keymat, u_char *meta, off_t mediasize, u_int sectorsize, u_int *nkey)
{
u_char *buf, buf1[16];
int error, e, i;
+ /* set up the key-material */
+ bcopy(keymat, sc->sha2, SHA512_DIGEST_LENGTH);
+
+ /* If passed-in metadata is non-zero, use it */
bzero(buf1, sizeof buf1);
if (bcmp(buf1, meta, sizeof buf1))
- return (g_bde_decrypt_lockx(sc, sbox, meta, mediasize,
+ return (g_bde_decrypt_lockx(sc, meta, mediasize,
sectorsize, nkey));
+ /* Read sector zero */
buf = g_read_data(sc->consumer, 0, sectorsize, &error);
if (buf == NULL)
return(error);
- error = 0;
+
+ /* Try each index in turn, save indicative errors for final result */
+ error = EINVAL;
for (i = 0; i < G_BDE_MAXKEYS; i++) {
- e = g_bde_decrypt_lockx(sc, sbox, buf + i * 16, mediasize,
+ e = g_bde_decrypt_lockx(sc, buf + i * 16, mediasize,
sectorsize, nkey);
+ /* Success or destroyed master key terminates */
if (e == 0 || e == ENOENT) {
error = e;
break;
}
- if (e == ESRCH)
- error = ENOTDIR;
- else if (e != 0)
+ if (e != 0 && error == EINVAL)
error = e;
}
g_free(buf);
diff --git a/sys/geom/bde/g_bde_work.c b/sys/geom/bde/g_bde_work.c
index 8b9942d..5211771 100644
--- a/sys/geom/bde/g_bde_work.c
+++ b/sys/geom/bde/g_bde_work.c
@@ -16,9 +16,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. The names of the authors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -70,9 +67,12 @@
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
+#include <sys/time.h>
#include <sys/proc.h>
#include <sys/kthread.h>
+#include <crypto/rijndael/rijndael.h>
+#include <crypto/sha2/sha2.h>
#include <geom/geom.h>
#include <geom/bde/g_bde.h>
@@ -81,6 +81,7 @@ static struct g_bde_sector * g_bde_new_sector(struct g_bde_work *wp, u_int len);
static void g_bde_release_sector(struct g_bde_work *wp, struct g_bde_sector *sp);
static struct g_bde_sector *g_bde_get_sector(struct g_bde_work *wp, off_t offset);
static int g_bde_start_read(struct g_bde_sector *sp);
+static void g_bde_purge_sector(struct g_bde_softc *sc, int fraction);
/*
* Work item allocation.
@@ -181,6 +182,20 @@ g_bde_new_sector(struct g_bde_work *wp, u_int len)
static u_int g_bde_ncache;
SYSCTL_UINT(_debug, OID_AUTO, gbde_ncache, CTLFLAG_RD, &g_bde_ncache, 0, "");
+static void
+g_bde_purge_one_sector(struct g_bde_softc *sc, struct g_bde_sector *sp)
+{
+
+ g_trace(G_T_TOPOLOGY, "g_bde_purge_one_sector(%p, %p)", sc, sp);
+ if (sp->ref != 0)
+ return;
+ TAILQ_REMOVE(&sc->freelist, sp, list);
+ g_bde_ncache--;
+ sc->ncache--;
+ bzero(sp->data, sp->size);
+ g_bde_delete_sector(sc, sp);
+}
+
static struct g_bde_sector *
g_bde_get_sector(struct g_bde_work *wp, off_t offset)
{
@@ -189,6 +204,14 @@ g_bde_get_sector(struct g_bde_work *wp, off_t offset)
g_trace(G_T_TOPOLOGY, "g_bde_get_sector(%p, %jd)", wp, (intmax_t)offset);
sc = wp->softc;
+
+ if (malloc_last_fail() < g_bde_ncache)
+ g_bde_purge_sector(sc, -1);
+
+ sp = TAILQ_FIRST(&sc->freelist);
+ if (sp != NULL && sp->ref == 0 && sp->used + 300 < time_uptime)
+ g_bde_purge_one_sector(sc, sp);
+
TAILQ_FOREACH(sp, &sc->freelist, list) {
if (sp->offset == offset)
break;
@@ -200,7 +223,12 @@ g_bde_get_sector(struct g_bde_work *wp, off_t offset)
if (sp->ref == 1)
sp->owner = wp;
} else {
- if (!TAILQ_EMPTY(&sc->freelist))
+ if (malloc_last_fail() < g_bde_ncache) {
+ TAILQ_FOREACH(sp, &sc->freelist, list)
+ if (sp->ref == 0)
+ break;
+ }
+ if (sp == NULL && !TAILQ_EMPTY(&sc->freelist))
sp = TAILQ_FIRST(&sc->freelist);
if (sp != NULL && sp->ref > 0)
sp = NULL;
@@ -227,6 +255,12 @@ g_bde_get_sector(struct g_bde_work *wp, off_t offset)
TAILQ_INSERT_TAIL(&sc->freelist, sp, list);
}
wp->ksp = sp;
+ if (sp == NULL) {
+ g_bde_purge_sector(sc, -1);
+ sp = g_bde_get_sector(wp, offset);
+ }
+ if (sp != NULL)
+ sp->used = time_uptime;
KASSERT(sp != NULL, ("get_sector failed"));
return(sp);
}
@@ -273,7 +307,14 @@ g_bde_purge_sector(struct g_bde_softc *sc, int fraction)
int n;
g_trace(G_T_TOPOLOGY, "g_bde_purge_sector(%p)", sc);
- n = sc->ncache / fraction + 1;
+ if (fraction > 0)
+ n = sc->ncache / fraction + 1;
+ else
+ n = g_bde_ncache - malloc_last_fail();
+ if (n < 0)
+ return;
+ if (n > sc->ncache)
+ n = sc->ncache;
while(n--) {
TAILQ_FOREACH(sp, &sc->freelist, list) {
if (sp->ref != 0)
OpenPOWER on IntegriCloud