diff options
author | pjd <pjd@FreeBSD.org> | 2006-06-05 21:38:54 +0000 |
---|---|---|
committer | pjd <pjd@FreeBSD.org> | 2006-06-05 21:38:54 +0000 |
commit | 3af66839d055bd726b45212e94f3c92606f3e7d8 (patch) | |
tree | b1352efa0d8f7422e30192f24f7c57d6f1635386 /sys/geom/eli/g_eli.c | |
parent | c7f4418287d976dea4735bd1459becbe46c8d515 (diff) | |
download | FreeBSD-src-3af66839d055bd726b45212e94f3c92606f3e7d8.zip FreeBSD-src-3af66839d055bd726b45212e94f3c92606f3e7d8.tar.gz |
Implement data integrity verification (data authentication) for geli(8).
Supported by: Wheel Sp. z o.o. (http://www.wheel.pl)
Diffstat (limited to 'sys/geom/eli/g_eli.c')
-rw-r--r-- | sys/geom/eli/g_eli.c | 371 |
1 files changed, 121 insertions, 250 deletions
diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c index e6e872a..47d6a0a 100644 --- a/sys/geom/eli/g_eli.c +++ b/sys/geom/eli/g_eli.c @@ -76,10 +76,13 @@ static u_int g_eli_threads = 0; TUNABLE_INT("kern.geom.eli.threads", &g_eli_threads); SYSCTL_UINT(_kern_geom_eli, OID_AUTO, threads, CTLFLAG_RW, &g_eli_threads, 0, "Number of threads doing crypto work"); +u_int g_eli_batch = 0; +TUNABLE_INT("kern.geom.eli.batch", &g_eli_batch); +SYSCTL_UINT(_kern_geom_eli, OID_AUTO, batch, CTLFLAG_RW, &g_eli_batch, 0, + "Use crypto operations batching"); static int g_eli_destroy_geom(struct gctl_req *req, struct g_class *mp, struct g_geom *gp); -static void g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp); static g_taste_t g_eli_taste; static g_dumpconf_t g_eli_dumpconf; @@ -107,7 +110,7 @@ struct g_class g_eli_class = { * accelerator or something like this. * The function updates the SID and rerun the operation. */ -static int +int g_eli_crypto_rerun(struct cryptop *crp) { struct g_eli_softc *sc; @@ -140,7 +143,7 @@ g_eli_crypto_rerun(struct cryptop *crp) * * g_eli_start -> g_io_request -> G_ELI_READ_DONE -> g_eli_crypto_run -> g_eli_crypto_read_done -> g_io_deliver */ -static void +void g_eli_read_done(struct bio *bp) { struct g_eli_softc *sc; @@ -150,10 +153,20 @@ g_eli_read_done(struct bio *bp) pbp = bp->bio_parent; if (pbp->bio_error == 0) pbp->bio_error = bp->bio_error; + /* + * Do we have all sectors already? + */ + pbp->bio_inbed++; + if (pbp->bio_inbed < pbp->bio_children) + return; g_destroy_bio(bp); if (pbp->bio_error != 0) { G_ELI_LOGREQ(0, pbp, "%s() failed", __func__); pbp->bio_completed = 0; + if (pbp->bio_driver2 != NULL) { + free(pbp->bio_driver2, M_ELI); + pbp->bio_driver2 = NULL; + } g_io_deliver(pbp, pbp->bio_error); return; } @@ -165,69 +178,30 @@ g_eli_read_done(struct bio *bp) } /* - * The function is called after we read and decrypt data. - * - * g_eli_start -> g_io_request -> g_eli_read_done -> g_eli_crypto_run -> G_ELI_CRYPTO_READ_DONE -> g_io_deliver - */ -static int -g_eli_crypto_read_done(struct cryptop *crp) -{ - struct bio *bp; - - if (crp->crp_etype == EAGAIN) { - if (g_eli_crypto_rerun(crp) == 0) - return (0); - } - bp = (struct bio *)crp->crp_opaque; - bp->bio_inbed++; - if (crp->crp_etype == 0) { - G_ELI_DEBUG(3, "Crypto READ request done (%d/%d).", - bp->bio_inbed, bp->bio_children); - bp->bio_completed += crp->crp_olen; - } else { - G_ELI_DEBUG(1, "Crypto READ request failed (%d/%d) error=%d.", - bp->bio_inbed, bp->bio_children, crp->crp_etype); - if (bp->bio_error == 0) - bp->bio_error = crp->crp_etype; - } - /* - * Do we have all sectors already? - */ - if (bp->bio_inbed < bp->bio_children) - return (0); - free(bp->bio_driver2, M_ELI); - bp->bio_driver2 = NULL; - if (bp->bio_error != 0) { - G_ELI_LOGREQ(0, bp, "Crypto READ request failed (error=%d).", - bp->bio_error); - bp->bio_completed = 0; - } - /* - * Read is finished, send it up. - */ - g_io_deliver(bp, bp->bio_error); - return (0); -} - -/* * The function is called after we encrypt and write data. * * g_eli_start -> g_eli_crypto_run -> g_eli_crypto_write_done -> g_io_request -> G_ELI_WRITE_DONE -> g_io_deliver */ -static void +void g_eli_write_done(struct bio *bp) { struct bio *pbp; G_ELI_LOGREQ(2, bp, "Request done."); pbp = bp->bio_parent; - if (pbp->bio_error == 0) - pbp->bio_error = bp->bio_error; + if (pbp->bio_error == 0) { + if (bp->bio_error != 0) + pbp->bio_error = bp->bio_error; + } + /* + * Do we have all sectors already? + */ + pbp->bio_inbed++; + if (pbp->bio_inbed < pbp->bio_children) + return; free(pbp->bio_driver2, M_ELI); pbp->bio_driver2 = NULL; - if (pbp->bio_error == 0) - pbp->bio_completed = pbp->bio_length; - else { + if (pbp->bio_error != 0) { G_ELI_LOGREQ(0, pbp, "Crypto WRITE request failed (error=%d).", pbp->bio_error); pbp->bio_completed = 0; @@ -236,68 +210,11 @@ g_eli_write_done(struct bio *bp) /* * Write is finished, send it up. */ + pbp->bio_completed = pbp->bio_length; g_io_deliver(pbp, pbp->bio_error); } /* - * The function is called after data encryption. - * - * g_eli_start -> g_eli_crypto_run -> G_ELI_CRYPTO_WRITE_DONE -> g_io_request -> g_eli_write_done -> g_io_deliver - */ -static int -g_eli_crypto_write_done(struct cryptop *crp) -{ - struct g_geom *gp; - struct g_consumer *cp; - struct bio *bp, *cbp; - - if (crp->crp_etype == EAGAIN) { - if (g_eli_crypto_rerun(crp) == 0) - return (0); - } - bp = (struct bio *)crp->crp_opaque; - bp->bio_inbed++; - if (crp->crp_etype == 0) { - G_ELI_DEBUG(3, "Crypto WRITE request done (%d/%d).", - bp->bio_inbed, bp->bio_children); - } else { - G_ELI_DEBUG(1, "Crypto WRITE request failed (%d/%d) error=%d.", - bp->bio_inbed, bp->bio_children, crp->crp_etype); - if (bp->bio_error == 0) - bp->bio_error = crp->crp_etype; - } - /* - * All sectors are already encrypted? - */ - if (bp->bio_inbed < bp->bio_children) - return (0); - bp->bio_inbed = 0; - bp->bio_children = 1; - cbp = bp->bio_driver1; - bp->bio_driver1 = NULL; - if (bp->bio_error != 0) { - G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).", - bp->bio_error); - free(bp->bio_driver2, M_ELI); - bp->bio_driver2 = NULL; - g_destroy_bio(cbp); - g_io_deliver(bp, bp->bio_error); - return (0); - } - cbp->bio_data = bp->bio_driver2; - cbp->bio_done = g_eli_write_done; - gp = bp->bio_to->geom; - cp = LIST_FIRST(&gp->consumer); - cbp->bio_to = cp->provider; - G_ELI_LOGREQ(2, cbp, "Sending request."); - /* - * Send encrypted data to the provider. - */ - g_io_request(cbp, cp); - return (0); -} - -/* * This function should never be called, but GEOM made as it set ->orphan() * method for every geom. */ @@ -359,15 +276,20 @@ g_eli_start(struct bio *bp) } switch (bp->bio_cmd) { case BIO_READ: - cbp->bio_done = g_eli_read_done; - cp = LIST_FIRST(&sc->sc_geom->consumer); - cbp->bio_to = cp->provider; - G_ELI_LOGREQ(2, cbp, "Sending request."); - /* - * Read encrypted data from provider. - */ - g_io_request(cbp, cp); - break; + if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) { + bp->bio_driver2 = NULL; + cbp->bio_done = g_eli_read_done; + cp = LIST_FIRST(&sc->sc_geom->consumer); + cbp->bio_to = cp->provider; + G_ELI_LOGREQ(2, cbp, "Sending request."); + /* + * Read encrypted data from provider. + */ + g_io_request(cbp, cp); + break; + } + bp->bio_pflags = 255; + /* FALLTHROUGH */ case BIO_WRITE: bp->bio_driver1 = cbp; mtx_lock(&sc->sc_queue_mtx); @@ -412,7 +334,7 @@ g_eli_worker(void *arg) mtx_lock(&sc->sc_queue_mtx); bp = bioq_takefirst(&sc->sc_queue); if (bp == NULL) { - if ((sc->sc_flags & G_ELI_FLAG_DESTROY) != 0) { + if (sc->sc_flags & G_ELI_FLAG_DESTROY) { LIST_REMOVE(wr, w_next); crypto_freesession(wr->w_sid); free(wr, M_ELI); @@ -427,14 +349,19 @@ g_eli_worker(void *arg) continue; } mtx_unlock(&sc->sc_queue_mtx); - g_eli_crypto_run(wr, bp); + if (bp->bio_cmd == BIO_READ && bp->bio_pflags == 255) + g_eli_auth_read(sc, bp); + else if (sc->sc_flags & G_ELI_FLAG_AUTH) + g_eli_auth_run(wr, bp); + else + g_eli_crypto_run(wr, bp); } } /* * Here we generate IV. It is unique for every sector. */ -static void +void g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv, size_t size) { @@ -448,110 +375,6 @@ g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv, bcopy(hash, iv, size); } -/* - * This is the main function responsible for cryptography (ie. communication - * with crypto(9) subsystem). - */ -static void -g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp) -{ - struct g_eli_softc *sc; - struct cryptop *crp; - struct cryptodesc *crd; - struct uio *uio; - struct iovec *iov; - u_int i, nsec, add, secsize; - int err, error; - size_t size; - u_char *p, *data; - - G_ELI_LOGREQ(3, bp, "%s", __func__); - - bp->bio_pflags = wr->w_number; - sc = wr->w_softc; - secsize = LIST_FIRST(&sc->sc_geom->provider)->sectorsize; - nsec = bp->bio_length / secsize; - - /* - * Calculate how much memory do we need. - * We need separate crypto operation for every single sector. - * It is much faster to calculate total amount of needed memory here and - * do the allocation once instead of allocating memory in pieces (many, - * many pieces). - */ - size = sizeof(*crp) * nsec; - size += sizeof(*crd) * nsec; - size += sizeof(*uio) * nsec; - size += sizeof(*iov) * nsec; - /* - * If we write the data we cannot destroy current bio_data content, - * so we need to allocate more memory for encrypted data. - */ - if (bp->bio_cmd == BIO_WRITE) - size += bp->bio_length; - p = malloc(size, M_ELI, M_WAITOK); - - bp->bio_inbed = 0; - bp->bio_children = nsec; - bp->bio_driver2 = p; - - if (bp->bio_cmd == BIO_READ) - data = bp->bio_data; - else { - data = p; - p += bp->bio_length; - bcopy(bp->bio_data, data, bp->bio_length); - } - - error = 0; - for (i = 0, add = 0; i < nsec; i++, add += secsize) { - crp = (struct cryptop *)p; p += sizeof(*crp); - crd = (struct cryptodesc *)p; p += sizeof(*crd); - uio = (struct uio *)p; p += sizeof(*uio); - iov = (struct iovec *)p; p += sizeof(*iov); - - iov->iov_len = secsize; - iov->iov_base = data; - data += secsize; - - uio->uio_iov = iov; - uio->uio_iovcnt = 1; - uio->uio_segflg = UIO_SYSSPACE; - uio->uio_resid = secsize; - - crp->crp_sid = wr->w_sid; - crp->crp_ilen = secsize; - crp->crp_olen = secsize; - crp->crp_opaque = (void *)bp; - crp->crp_buf = (void *)uio; - if (bp->bio_cmd == BIO_WRITE) - crp->crp_callback = g_eli_crypto_write_done; - else /* if (bp->bio_cmd == BIO_READ) */ - crp->crp_callback = g_eli_crypto_read_done; - crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIFSYNC | CRYPTO_F_REL; - crp->crp_desc = crd; - - crd->crd_skip = 0; - crd->crd_len = secsize; - crd->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; - if (bp->bio_cmd == BIO_WRITE) - crd->crd_flags |= CRD_F_ENCRYPT; - crd->crd_alg = sc->sc_algo; - crd->crd_key = sc->sc_datakey; - crd->crd_klen = sc->sc_keylen; - g_eli_crypto_ivgen(sc, bp->bio_offset + add, crd->crd_iv, - sizeof(crd->crd_iv)); - crd->crd_next = NULL; - - crp->crp_etype = 0; - err = crypto_dispatch(crp); - if (error == 0) - error = err; - } - if (bp->bio_error == 0) - bp->bio_error = error; -} - int g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, struct g_eli_metadata *md) @@ -659,7 +482,7 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, struct g_geom *gp; struct g_provider *pp; struct g_consumer *cp; - struct cryptoini cri; + struct cryptoini crie, cria; u_int i, threads; int error; @@ -688,15 +511,41 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, sc->sc_crypto = G_ELI_CRYPTO_SW; sc->sc_flags = md->md_flags; - sc->sc_algo = md->md_algo; + sc->sc_ealgo = md->md_ealgo; sc->sc_nkey = nkey; /* * Remember the keys in our softc structure. */ - bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey)); - mkey += sizeof(sc->sc_ivkey); - bcopy(mkey, sc->sc_datakey, sizeof(sc->sc_datakey)); - sc->sc_keylen = md->md_keylen; + 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; + sc->sc_aalgo = md->md_aalgo; + sc->sc_alen = g_eli_hashlen(sc->sc_aalgo); + + sc->sc_data_per_sector = bpp->sectorsize - sc->sc_alen; + /* + * Some hash functions (like SHA1 and RIPEMD160) generates hash + * which length is not multiple of 128 bits, but we want data + * length to be multiple of 128, so we can encrypt without + * padding. The line below rounds down data length to multiple + * of 128 bits. + */ + sc->sc_data_per_sector -= sc->sc_data_per_sector % 16; + + sc->sc_bytes_per_sector = + (md->md_sectorsize - 1) / sc->sc_data_per_sector + 1; + sc->sc_bytes_per_sector *= bpp->sectorsize; + /* + * Precalculate SHA256 for HMAC key 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_akeyctx); + SHA256_Update(&sc->sc_akeyctx, sc->sc_akey, + sizeof(sc->sc_akey)); + } /* * Precalculate SHA256 for IV generation. @@ -744,10 +593,17 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, LIST_INIT(&sc->sc_workers); - bzero(&cri, sizeof(cri)); - cri.cri_alg = sc->sc_algo; - cri.cri_klen = sc->sc_keylen; - cri.cri_key = sc->sc_datakey; + bzero(&crie, sizeof(crie)); + crie.cri_alg = sc->sc_ealgo; + crie.cri_klen = sc->sc_ekeylen; + crie.cri_key = sc->sc_ekey; + if (sc->sc_flags & G_ELI_FLAG_AUTH) { + bzero(&cria, sizeof(cria)); + cria.cri_alg = sc->sc_aalgo; + cria.cri_klen = sc->sc_akeylen; + cria.cri_key = sc->sc_akey; + crie.cri_next = &cria; + } threads = g_eli_threads; if (threads == 0) @@ -767,12 +623,12 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, * Use software cryptography, if we cannot get it. */ if (i == 0) { - error = crypto_newsession(&wr->w_sid, &cri, 1); + error = crypto_newsession(&wr->w_sid, &crie, 1); if (error == 0) sc->sc_crypto = G_ELI_CRYPTO_HW; } if (sc->sc_crypto == G_ELI_CRYPTO_SW) - error = crypto_newsession(&wr->w_sid, &cri, 0); + error = crypto_newsession(&wr->w_sid, &crie, 0); if (error != 0) { free(wr, M_ELI); if (req != NULL) { @@ -811,14 +667,22 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp, 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) == 0) + if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) pp->mediasize -= bpp->sectorsize; - pp->mediasize -= (pp->mediasize % pp->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; + } + g_error_provider(pp, 0); G_ELI_DEBUG(0, "Device %s created.", pp->name); - G_ELI_DEBUG(0, " Cipher: %s", g_eli_algo2str(sc->sc_algo)); - G_ELI_DEBUG(0, "Key length: %u", sc->sc_keylen); + G_ELI_DEBUG(0, "Encryption: %s %u", g_eli_algo2str(sc->sc_ealgo), + sc->sc_ekeylen); + if (sc->sc_flags & G_ELI_FLAG_AUTH) + G_ELI_DEBUG(0, " Integrity: %s", g_eli_algo2str(sc->sc_aalgo)); G_ELI_DEBUG(0, " Crypto: %s", sc->sc_crypto == G_ELI_CRYPTO_SW ? "software" : "hardware"); return (gp); @@ -996,7 +860,7 @@ g_eli_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) if (md.md_provsize != pp->mediasize) return (NULL); /* Should we attach it on boot? */ - if ((md.md_flags & G_ELI_FLAG_BOOT) == 0) + if (!(md.md_flags & G_ELI_FLAG_BOOT)) return (NULL); if (md.md_keys == 0x00) { G_ELI_DEBUG(0, "No valid keys on %s.", pp->name); @@ -1118,7 +982,7 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, int first = 1; #define ADD_FLAG(flag, name) do { \ - if ((sc->sc_flags & (flag)) != 0) { \ + if (sc->sc_flags & (flag)) { \ if (!first) \ sbuf_printf(sb, ", "); \ else \ @@ -1130,13 +994,14 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, ADD_FLAG(G_ELI_FLAG_BOOT, "BOOT"); ADD_FLAG(G_ELI_FLAG_WO_DETACH, "W-DETACH"); ADD_FLAG(G_ELI_FLAG_RW_DETACH, "RW-DETACH"); + ADD_FLAG(G_ELI_FLAG_AUTH, "AUTH"); ADD_FLAG(G_ELI_FLAG_WOPEN, "W-OPEN"); ADD_FLAG(G_ELI_FLAG_DESTROY, "DESTROY"); #undef ADD_FLAG } sbuf_printf(sb, "</Flags>\n"); - if ((sc->sc_flags & G_ELI_FLAG_ONETIME) == 0) { + if (!(sc->sc_flags & G_ELI_FLAG_ONETIME)) { sbuf_printf(sb, "%s<UsedKey>%u</UsedKey>\n", indent, sc->sc_nkey); } @@ -1153,9 +1018,15 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, break; } sbuf_printf(sb, "</Crypto>\n"); - sbuf_printf(sb, "%s<KeyLength>%u</KeyLength>\n", indent, sc->sc_keylen); - sbuf_printf(sb, "%s<Cipher>%s</Cipher>\n", indent, - g_eli_algo2str(sc->sc_algo)); + if (sc->sc_flags & G_ELI_FLAG_AUTH) { + sbuf_printf(sb, + "%s<AuthenticationAlgorithm>%s</AuthenticationAlgorithm>\n", + indent, g_eli_algo2str(sc->sc_aalgo)); + } + sbuf_printf(sb, "%s<KeyLength>%u</KeyLength>\n", indent, + sc->sc_ekeylen); + sbuf_printf(sb, "%s<EncryptionAlgorithm>%s</EncryptionAlgorithm>\n", indent, + g_eli_algo2str(sc->sc_ealgo)); } DECLARE_GEOM_CLASS(g_eli_class, g_eli); |