diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c index 4bcce3d41efa..c44cddc0240b 100644 --- a/sys/geom/eli/g_eli.c +++ b/sys/geom/eli/g_eli.c @@ -374,6 +374,34 @@ g_eli_worker(void *arg) } } +/* + * Select encryption key. If G_ELI_FLAG_SINGLE_KEY is present we only have one + * key available for all the data. If the flag is not present select the key + * based on data offset. + */ +uint8_t * +g_eli_crypto_key(struct g_eli_softc *sc, off_t offset, size_t blocksize) +{ + u_int nkey; + + if (sc->sc_nekeys == 1) + return (sc->sc_ekeys[0]); + + KASSERT(sc->sc_nekeys > 1, ("%s: sc_nekeys=%u", __func__, + sc->sc_nekeys)); + KASSERT((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0, + ("%s: SINGLE_KEY flag set, but sc_nekeys=%u", __func__, + sc->sc_nekeys)); + + /* We switch key every 2^G_ELI_KEY_SHIFT blocks. */ + nkey = (offset >> G_ELI_KEY_SHIFT) / blocksize; + + KASSERT(nkey < sc->sc_nekeys, ("%s: nkey=%u >= sc_nekeys=%u", __func__, + nkey, sc->sc_nekeys)); + + return (sc->sc_ekeys[nkey]); +} + /* * Here we generate IV. It is unique for every sector. */ @@ -548,13 +576,10 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, /* Backward compatibility. */ if (md->md_version < 4) sc->sc_flags |= G_ELI_FLAG_NATIVE_BYTE_ORDER; + if (md->md_version < 5) + sc->sc_flags |= G_ELI_FLAG_SINGLE_KEY; sc->sc_ealgo = md->md_ealgo; sc->sc_nkey = nkey; - /* - * Remember the keys in our softc structure. - */ - g_eli_mkey_propagate(sc, mkey); - sc->sc_ekeylen = md->md_keylen; if (sc->sc_flags & G_ELI_FLAG_AUTH) { sc->sc_akeylen = sizeof(sc->sc_akey) * 8; @@ -584,14 +609,6 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, sizeof(sc->sc_akey)); } - /* - * Precalculate SHA256 for IV generation. - * This is expensive operation and we can do it only once now or for - * every access to sector, so now will be much better. - */ - SHA256_Init(&sc->sc_ivctx); - SHA256_Update(&sc->sc_ivctx, sc->sc_ivkey, sizeof(sc->sc_ivkey)); - gp->softc = sc; sc->sc_geom = gp; @@ -633,12 +650,37 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, goto failed; } + sc->sc_sectorsize = md->md_sectorsize; + sc->sc_mediasize = bpp->mediasize; + if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) + sc->sc_mediasize -= bpp->sectorsize; + if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) + sc->sc_mediasize -= (sc->sc_mediasize % sc->sc_sectorsize); + else { + sc->sc_mediasize /= sc->sc_bytes_per_sector; + sc->sc_mediasize *= sc->sc_sectorsize; + } + + /* + * Remember the keys in our softc structure. + */ + g_eli_mkey_propagate(sc, mkey); + sc->sc_ekeylen = md->md_keylen; + + /* + * Precalculate SHA256 for IV generation. + * This is expensive operation and we can do it only once now or for + * every access to sector, so now will be much better. + */ + SHA256_Init(&sc->sc_ivctx); + SHA256_Update(&sc->sc_ivctx, sc->sc_ivkey, sizeof(sc->sc_ivkey)); + LIST_INIT(&sc->sc_workers); bzero(&crie, sizeof(crie)); crie.cri_alg = sc->sc_ealgo; crie.cri_klen = sc->sc_ekeylen; - crie.cri_key = sc->sc_ekey; + crie.cri_key = sc->sc_ekeys[0]; if (sc->sc_flags & G_ELI_FLAG_AUTH) { bzero(&cria, sizeof(cria)); cria.cri_alg = sc->sc_aalgo; @@ -715,16 +757,8 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, * Create decrypted provider. */ pp = g_new_providerf(gp, "%s%s", bpp->name, G_ELI_SUFFIX); - pp->sectorsize = md->md_sectorsize; - pp->mediasize = bpp->mediasize; - if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) - pp->mediasize -= bpp->sectorsize; - if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) - pp->mediasize -= (pp->mediasize % pp->sectorsize); - else { - pp->mediasize /= sc->sc_bytes_per_sector; - pp->mediasize *= pp->sectorsize; - } + pp->mediasize = sc->sc_mediasize; + pp->sectorsize = sc->sc_sectorsize; g_error_provider(pp, 0); @@ -755,6 +789,11 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, } g_destroy_consumer(cp); g_destroy_geom(gp); + if (sc->sc_ekeys != NULL) { + bzero(sc->sc_ekeys, + sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN)); + free(sc->sc_ekeys, M_ELI); + } bzero(sc, sizeof(*sc)); free(sc, M_ELI); return (NULL); @@ -794,6 +833,9 @@ g_eli_destroy(struct g_eli_softc *sc, boolean_t force) } mtx_destroy(&sc->sc_queue_mtx); gp->softc = NULL; + bzero(sc->sc_ekeys, + sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN)); + free(sc->sc_ekeys, M_ELI); bzero(sc, sizeof(*sc)); free(sc, M_ELI); @@ -1042,6 +1084,7 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, sbuf_printf(sb, name); \ } \ } while (0) + ADD_FLAG(G_ELI_FLAG_SINGLE_KEY, "SINGLE-KEY"); ADD_FLAG(G_ELI_FLAG_NATIVE_BYTE_ORDER, "NATIVE-BYTE-ORDER"); ADD_FLAG(G_ELI_FLAG_ONETIME, "ONETIME"); ADD_FLAG(G_ELI_FLAG_BOOT, "BOOT"); diff --git a/sys/geom/eli/g_eli.h b/sys/geom/eli/g_eli.h index 7c492e03afaa..ab0b2c9221af 100644 --- a/sys/geom/eli/g_eli.h +++ b/sys/geom/eli/g_eli.h @@ -60,8 +60,9 @@ * 3 - Added 'configure' subcommand. * 4 - IV is generated from offset converted to little-endian * (flag G_ELI_FLAG_NATIVE_BYTE_ORDER will be set for older versions). + * 5 - Added multiple encrypton keys. */ -#define G_ELI_VERSION 4 +#define G_ELI_VERSION 5 /* ON DISK FLAGS. */ /* Use random, onetime keys. */ @@ -83,6 +84,8 @@ #define G_ELI_FLAG_DESTROY 0x00020000 /* Provider uses native byte-order for IV generation. */ #define G_ELI_FLAG_NATIVE_BYTE_ORDER 0x00040000 +/* Provider uses single encryption key. */ +#define G_ELI_FLAG_SINGLE_KEY 0x00080000 #define SHA512_MDLEN 64 #define G_ELI_AUTH_SECKEYLEN SHA256_DIGEST_LENGTH @@ -98,6 +101,8 @@ /* Data-Key, IV-Key, HMAC_SHA512(Derived-Key, Data-Key+IV-Key) */ #define G_ELI_MKEYLEN (G_ELI_DATAIVKEYLEN + SHA512_MDLEN) #define G_ELI_OVERWRITES 5 +/* Switch data encryption key every 2^20 blocks. */ +#define G_ELI_KEY_SHIFT 20 #ifdef _KERNEL extern u_int g_eli_debug; @@ -139,27 +144,30 @@ struct g_eli_worker { }; struct g_eli_softc { - struct g_geom *sc_geom; - u_int sc_crypto; - uint8_t sc_mkey[G_ELI_DATAIVKEYLEN]; - uint8_t sc_ekey[G_ELI_DATAKEYLEN]; - u_int sc_ealgo; - u_int sc_ekeylen; - uint8_t sc_akey[G_ELI_AUTHKEYLEN]; - u_int sc_aalgo; - u_int sc_akeylen; - u_int sc_alen; - SHA256_CTX sc_akeyctx; - uint8_t sc_ivkey[G_ELI_IVKEYLEN]; - SHA256_CTX sc_ivctx; - int sc_nkey; - uint32_t sc_flags; - u_int sc_bytes_per_sector; - u_int sc_data_per_sector; + struct g_geom *sc_geom; + u_int sc_crypto; + uint8_t sc_mkey[G_ELI_DATAIVKEYLEN]; + uint8_t **sc_ekeys; + u_int sc_nekeys; + u_int sc_ealgo; + u_int sc_ekeylen; + uint8_t sc_akey[G_ELI_AUTHKEYLEN]; + u_int sc_aalgo; + u_int sc_akeylen; + u_int sc_alen; + SHA256_CTX sc_akeyctx; + uint8_t sc_ivkey[G_ELI_IVKEYLEN]; + SHA256_CTX sc_ivctx; + int sc_nkey; + uint32_t sc_flags; + off_t sc_mediasize; + size_t sc_sectorsize; + u_int sc_bytes_per_sector; + u_int sc_data_per_sector; /* Only for software cryptography. */ struct bio_queue_head sc_queue; - struct mtx sc_queue_mtx; + struct mtx sc_queue_mtx; LIST_HEAD(, g_eli_worker) sc_workers; }; #define sc_name sc_geom->name @@ -231,7 +239,7 @@ eli_metadata_decode_v0(const u_char *data, struct g_eli_metadata *md) } static __inline int -eli_metadata_decode_v1v2v3v4(const u_char *data, struct g_eli_metadata *md) +eli_metadata_decode_v1v2v3v4v5(const u_char *data, struct g_eli_metadata *md) { MD5_CTX ctx; const u_char *p; @@ -269,7 +277,8 @@ eli_metadata_decode(const u_char *data, struct g_eli_metadata *md) case 2: case 3: case 4: - error = eli_metadata_decode_v1v2v3v4(data, md); + case 5: + error = eli_metadata_decode_v1v2v3v4v5(data, md); break; default: error = EINVAL; @@ -461,6 +470,8 @@ void g_eli_config(struct gctl_req *req, struct g_class *mp, const char *verb); void g_eli_read_done(struct bio *bp); void g_eli_write_done(struct bio *bp); int g_eli_crypto_rerun(struct cryptop *crp); +uint8_t *g_eli_crypto_key(struct g_eli_softc *sc, off_t offset, + size_t blocksize); void g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv, size_t size); diff --git a/sys/geom/eli/g_eli_integrity.c b/sys/geom/eli/g_eli_integrity.c index b9a3f31359ad..8b98e213b406 100644 --- a/sys/geom/eli/g_eli_integrity.c +++ b/sys/geom/eli/g_eli_integrity.c @@ -507,7 +507,7 @@ g_eli_auth_run(struct g_eli_worker *wr, struct bio *bp) if (bp->bio_cmd == BIO_WRITE) crde->crd_flags |= CRD_F_ENCRYPT; crde->crd_alg = sc->sc_ealgo; - crde->crd_key = sc->sc_ekey; + crde->crd_key = g_eli_crypto_key(sc, dstoff, encr_secsize); crde->crd_klen = sc->sc_ekeylen; g_eli_crypto_ivgen(sc, dstoff, crde->crd_iv, sizeof(crde->crd_iv)); diff --git a/sys/geom/eli/g_eli_key.c b/sys/geom/eli/g_eli_key.c index 5b024d787cca..230ae5c81e2c 100644 --- a/sys/geom/eli/g_eli_key.c +++ b/sys/geom/eli/g_eli_key.c @@ -43,6 +43,9 @@ __FBSDID("$FreeBSD$"); #include +#ifdef _KERNEL +MALLOC_DECLARE(M_ELI); +#endif /* * Verify if the given 'key' is correct. @@ -178,6 +181,46 @@ g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen, } #ifdef _KERNEL +static void +g_eli_ekeys_generate(struct g_eli_softc *sc) +{ + uint8_t *keys; + u_int kno; + off_t mediasize; + size_t blocksize; + struct { + char magic[4]; + uint8_t keyno[8]; + } __packed hmacdata; + + KASSERT((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0, + ("%s: G_ELI_FLAG_SINGLE_KEY flag present", __func__)); + + if ((sc->sc_flags & G_ELI_FLAG_AUTH) != 0) { + struct g_provider *pp; + + pp = LIST_FIRST(&sc->sc_geom->consumer)->provider; + mediasize = pp->mediasize; + blocksize = pp->sectorsize; + } else { + mediasize = sc->sc_mediasize; + blocksize = sc->sc_sectorsize; + } + sc->sc_nekeys = ((mediasize - 1) >> G_ELI_KEY_SHIFT) / blocksize + 1; + sc->sc_ekeys = + malloc(sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN), + M_ELI, M_WAITOK); + keys = (uint8_t *)(sc->sc_ekeys + sc->sc_nekeys); + bcopy("ekey", hmacdata.magic, 4); + for (kno = 0; kno < sc->sc_nekeys; kno++, keys += G_ELI_DATAKEYLEN) { + sc->sc_ekeys[kno] = keys; + le64enc(hmacdata.keyno, (uint64_t)kno); + g_eli_crypto_hmac(sc->sc_mkey, G_ELI_MAXKEYLEN, + (uint8_t *)&hmacdata, sizeof(hmacdata), + sc->sc_ekeys[kno], 0); + } +} + /* * When doing encryption only, copy IV key and encryption key. * When doing encryption and authentication, copy IV key, generate encryption @@ -193,16 +236,33 @@ g_eli_mkey_propagate(struct g_eli_softc *sc, const unsigned char *mkey) bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey)); mkey += sizeof(sc->sc_ivkey); - if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) { - bcopy(mkey, sc->sc_ekey, sizeof(sc->sc_ekey)); + /* + * The authentication key is: akey = HMAC_SHA512(Master-Key, 0x11) + */ + if ((sc->sc_flags & G_ELI_FLAG_AUTH) != 0) { + g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x11", 1, + sc->sc_akey, 0); } else { - /* - * The encryption key is: ekey = HMAC_SHA512(Master-Key, 0x10) - * The authentication key is: akey = HMAC_SHA512(Master-Key, 0x11) - */ - g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x10", 1, sc->sc_ekey, 0); - g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x11", 1, sc->sc_akey, 0); + arc4rand(sc->sc_akey, sizeof(sc->sc_akey), 0); } + if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) { + sc->sc_nekeys = 1; + sc->sc_ekeys = malloc(sc->sc_nekeys * + (sizeof(uint8_t *) + G_ELI_DATAKEYLEN), M_ELI, M_WAITOK); + sc->sc_ekeys[0] = (uint8_t *)(sc->sc_ekeys + sc->sc_nekeys); + if ((sc->sc_flags & G_ELI_FLAG_AUTH) == 0) + bcopy(mkey, sc->sc_ekeys[0], G_ELI_DATAKEYLEN); + else { + /* + * The encryption key is: ekey = HMAC_SHA512(Master-Key, 0x10) + */ + g_eli_crypto_hmac(mkey, G_ELI_MAXKEYLEN, "\x10", 1, + sc->sc_ekeys[0], 0); + } + } else { + /* Generate all encryption keys. */ + g_eli_ekeys_generate(sc); + } } #endif diff --git a/sys/geom/eli/g_eli_privacy.c b/sys/geom/eli/g_eli_privacy.c index 59517471a7d0..7a59d33fb2e0 100644 --- a/sys/geom/eli/g_eli_privacy.c +++ b/sys/geom/eli/g_eli_privacy.c @@ -252,10 +252,12 @@ g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp) crd->crd_skip = 0; crd->crd_len = secsize; crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; + if (sc->sc_nekeys > 1) + crd->crd_flags |= CRD_F_KEY_EXPLICIT; if (bp->bio_cmd == BIO_WRITE) crd->crd_flags |= CRD_F_ENCRYPT; crd->crd_alg = sc->sc_ealgo; - crd->crd_key = sc->sc_ekey; + crd->crd_key = g_eli_crypto_key(sc, dstoff, secsize); crd->crd_klen = sc->sc_ekeylen; g_eli_crypto_ivgen(sc, dstoff, crd->crd_iv, sizeof(crd->crd_iv));