diff options
author | phk <phk@FreeBSD.org> | 2002-11-04 09:27:01 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2002-11-04 09:27:01 +0000 |
commit | 16874ad9231c653cc0923d057079cb01dc655144 (patch) | |
tree | f63c9d7688c826251a2cc69a8cbf9eace1f02f75 /sys/geom/bde | |
parent | 8f58d0f543eb1d2ede1aa0736e9fe5e958cba1d6 (diff) | |
download | FreeBSD-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/geom/bde')
-rw-r--r-- | sys/geom/bde/g_bde.c | 16 | ||||
-rw-r--r-- | sys/geom/bde/g_bde.h | 95 | ||||
-rw-r--r-- | sys/geom/bde/g_bde_crypt.c | 57 | ||||
-rw-r--r-- | sys/geom/bde/g_bde_lock.c | 454 | ||||
-rw-r--r-- | sys/geom/bde/g_bde_work.c | 51 |
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 *)§or, 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 *)§or, 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) |