summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/geom/eli/g_eli.c51
-rw-r--r--sys/geom/eli/g_eli.h61
-rw-r--r--sys/geom/eli/g_eli_ctl.c7
-rw-r--r--sys/geom/eli/g_eli_integrity.c10
-rw-r--r--sys/geom/eli/g_eli_key.c62
-rw-r--r--sys/geom/eli/g_eli_key_cache.c319
-rw-r--r--sys/geom/eli/g_eli_privacy.c14
-rw-r--r--sys/modules/geom/geom_eli/Makefile1
8 files changed, 387 insertions, 138 deletions
diff --git a/sys/geom/eli/g_eli.c b/sys/geom/eli/g_eli.c
index 9f712cf..8edd147 100644
--- a/sys/geom/eli/g_eli.c
+++ b/sys/geom/eli/g_eli.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -329,7 +329,7 @@ g_eli_newsession(struct g_eli_worker *wr)
crie.cri_klen = sc->sc_ekeylen;
if (sc->sc_ealgo == CRYPTO_AES_XTS)
crie.cri_klen <<= 1;
- crie.cri_key = sc->sc_ekeys[0];
+ crie.cri_key = sc->sc_ekey;
if (sc->sc_flags & G_ELI_FLAG_AUTH) {
bzero(&cria, sizeof(cria));
cria.cri_alg = sc->sc_aalgo;
@@ -522,34 +522,6 @@ again:
}
/*
- * 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.
*/
void
@@ -766,6 +738,7 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp,
bioq_init(&sc->sc_queue);
mtx_init(&sc->sc_queue_mtx, "geli:queue", NULL, MTX_DEF);
+ mtx_init(&sc->sc_ekeys_lock, "geli:ekeys", NULL, MTX_DEF);
pp = NULL;
cp = g_new_consumer(gp);
@@ -909,11 +882,7 @@ failed:
}
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);
- }
+ g_eli_key_destroy(sc);
bzero(sc, sizeof(*sc));
free(sc, M_ELI);
return (NULL);
@@ -953,12 +922,7 @@ g_eli_destroy(struct g_eli_softc *sc, boolean_t force)
}
mtx_destroy(&sc->sc_queue_mtx);
gp->softc = NULL;
- if (sc->sc_ekeys != NULL) {
- /* The sc_ekeys field can be NULL is device is suspended. */
- bzero(sc->sc_ekeys,
- sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN));
- free(sc->sc_ekeys, M_ELI);
- }
+ g_eli_key_destroy(sc);
bzero(sc, sizeof(*sc));
free(sc, M_ELI);
@@ -1191,6 +1155,11 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
return;
if (pp != NULL || cp != NULL)
return; /* Nothing here. */
+
+ sbuf_printf(sb, "%s<KeysTotal>%ju</KeysTotal>", indent,
+ (uintmax_t)sc->sc_ekeys_total);
+ sbuf_printf(sb, "%s<KeysAllocated>%ju</KeysAllocated>", indent,
+ (uintmax_t)sc->sc_ekeys_allocated);
sbuf_printf(sb, "%s<Flags>", indent);
if (sc->sc_flags == 0)
sbuf_printf(sb, "NONE");
diff --git a/sys/geom/eli/g_eli.h b/sys/geom/eli/g_eli.h
index d67e436..e0fa616 100644
--- a/sys/geom/eli/g_eli.h
+++ b/sys/geom/eli/g_eli.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -37,6 +37,10 @@
#ifdef _KERNEL
#include <sys/bio.h>
#include <sys/libkern.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
#include <geom/geom.h>
#else
#include <stdio.h>
@@ -150,31 +154,35 @@ 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_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;
- int sc_inflight;
- off_t sc_mediasize;
- size_t sc_sectorsize;
- 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_ekey[G_ELI_DATAKEYLEN];
+ TAILQ_HEAD(, g_eli_key) sc_ekeys_queue;
+ RB_HEAD(g_eli_key_tree, g_eli_key) sc_ekeys_tree;
+ struct mtx sc_ekeys_lock;
+ uint64_t sc_ekeys_total;
+ uint64_t sc_ekeys_allocated;
+ 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;
+ int sc_inflight;
+ 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
@@ -539,4 +547,11 @@ void g_eli_crypto_hmac_update(struct hmac_ctx *ctx, const uint8_t *data,
void g_eli_crypto_hmac_final(struct hmac_ctx *ctx, uint8_t *md, size_t mdsize);
void g_eli_crypto_hmac(const uint8_t *hkey, size_t hkeysize,
const uint8_t *data, size_t datasize, uint8_t *md, size_t mdsize);
+
+#ifdef _KERNEL
+void g_eli_key_init(struct g_eli_softc *sc);
+void g_eli_key_destroy(struct g_eli_softc *sc);
+uint8_t *g_eli_key_hold(struct g_eli_softc *sc, off_t offset, size_t blocksize);
+void g_eli_key_drop(struct g_eli_softc *sc, uint8_t *rawkey);
+#endif
#endif /* !_G_ELI_H_ */
diff --git a/sys/geom/eli/g_eli_ctl.c b/sys/geom/eli/g_eli_ctl.c
index 2413918..a5de30b 100644
--- a/sys/geom/eli/g_eli_ctl.c
+++ b/sys/geom/eli/g_eli_ctl.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -739,10 +739,7 @@ g_eli_suspend_one(struct g_eli_softc *sc, struct gctl_req *req)
* Clear sensitive data on suspend, they will be recovered on resume.
*/
bzero(sc->sc_mkey, sizeof(sc->sc_mkey));
- bzero(sc->sc_ekeys,
- sc->sc_nekeys * (sizeof(uint8_t *) + G_ELI_DATAKEYLEN));
- free(sc->sc_ekeys, M_ELI);
- sc->sc_ekeys = NULL;
+ g_eli_key_destroy(sc);
bzero(sc->sc_akey, sizeof(sc->sc_akey));
bzero(&sc->sc_akeyctx, sizeof(sc->sc_akeyctx));
bzero(sc->sc_ivkey, sizeof(sc->sc_ivkey));
diff --git a/sys/geom/eli/g_eli_integrity.c b/sys/geom/eli/g_eli_integrity.c
index 24586bd..d37bacb 100644
--- a/sys/geom/eli/g_eli_integrity.c
+++ b/sys/geom/eli/g_eli_integrity.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -148,12 +148,13 @@ g_eli_auth_read_done(struct cryptop *crp)
if (bp->bio_error == 0)
bp->bio_error = crp->crp_etype;
}
+ sc = bp->bio_to->geom->softc;
+ g_eli_key_drop(sc, crp->crp_desc->crd_key);
/*
* Do we have all sectors already?
*/
if (bp->bio_inbed < bp->bio_children)
return (0);
- sc = bp->bio_to->geom->softc;
if (bp->bio_error == 0) {
u_int i, lsec, nsec, data_secsize, decr_secsize, encr_secsize;
u_char *srcdata, *dstdata, *auth;
@@ -272,12 +273,13 @@ g_eli_auth_write_done(struct cryptop *crp)
if (bp->bio_error == 0)
bp->bio_error = crp->crp_etype;
}
+ sc = bp->bio_to->geom->softc;
+ g_eli_key_drop(sc, crp->crp_desc->crd_key);
/*
* All sectors are already encrypted?
*/
if (bp->bio_inbed < bp->bio_children)
return (0);
- sc = bp->bio_to->geom->softc;
if (bp->bio_error != 0) {
G_ELI_LOGREQ(0, bp, "Crypto WRITE request failed (error=%d).",
bp->bio_error);
@@ -514,7 +516,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 = g_eli_crypto_key(sc, dstoff, encr_secsize);
+ crde->crd_key = g_eli_key_hold(sc, dstoff, encr_secsize);
crde->crd_klen = sc->sc_ekeylen;
if (sc->sc_ealgo == CRYPTO_AES_XTS)
crde->crd_klen <<= 1;
diff --git a/sys/geom/eli/g_eli_key.c b/sys/geom/eli/g_eli_key.c
index 37b2ad3..3696e2e 100644
--- a/sys/geom/eli/g_eli_key.c
+++ b/sys/geom/eli/g_eli_key.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -181,46 +181,6 @@ 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
@@ -246,24 +206,8 @@ g_eli_mkey_propagate(struct g_eli_softc *sc, const unsigned char *mkey)
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);
- }
+ /* Initialize encryption keys. */
+ g_eli_key_init(sc);
if (sc->sc_flags & G_ELI_FLAG_AUTH) {
/*
diff --git a/sys/geom/eli/g_eli_key_cache.c b/sys/geom/eli/g_eli_key_cache.c
new file mode 100644
index 0000000..eab45ac
--- /dev/null
+++ b/sys/geom/eli/g_eli_key_cache.c
@@ -0,0 +1,319 @@
+/*-
+ * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/tree.h>
+
+#include <geom/geom.h>
+
+#include <geom/eli/g_eli.h>
+
+MALLOC_DECLARE(M_ELI);
+
+SYSCTL_DECL(_kern_geom_eli);
+/*
+ * The default limit (8192 keys) will allow to cache all keys for 4TB
+ * provider with 512 bytes sectors and will take around 1MB of memory.
+ */
+static u_int g_eli_key_cache_limit = 8192;
+TUNABLE_INT("kern.geom.eli.key_cache_limit", &g_eli_key_cache_limit);
+SYSCTL_UINT(_kern_geom_eli, OID_AUTO, key_cache_limit, CTLFLAG_RDTUN,
+ &g_eli_key_cache_limit, 0, "Maximum number of encryption keys to cache");
+static uint64_t g_eli_key_cache_hits;
+SYSCTL_UQUAD(_kern_geom_eli, OID_AUTO, key_cache_hits, CTLFLAG_RW,
+ &g_eli_key_cache_hits, 0, "Key cache hits");
+static uint64_t g_eli_key_cache_misses;
+SYSCTL_UQUAD(_kern_geom_eli, OID_AUTO, key_cache_misses, CTLFLAG_RW,
+ &g_eli_key_cache_misses, 0, "Key cache misses");
+
+struct g_eli_key {
+ /* Key value, must be first in the structure. */
+ uint8_t gek_key[G_ELI_DATAKEYLEN];
+ /* Key number. */
+ uint64_t gek_keyno;
+ /* Reference counter. */
+ int gek_count;
+ /* Keeps keys sorted by most recent use. */
+ TAILQ_ENTRY(g_eli_key) gek_next;
+ /* Keeps keys sorted by number. */
+ RB_ENTRY(g_eli_key) gek_link;
+};
+
+static int
+g_eli_key_cmp(const struct g_eli_key *a, const struct g_eli_key *b)
+{
+
+ if (a->gek_keyno > b->gek_keyno)
+ return (1);
+ else if (a->gek_keyno < b->gek_keyno)
+ return (-1);
+ return (0);
+}
+
+RB_PROTOTYPE(g_eli_key_tree, g_eli_key, gek_link, g_eli_key_cmp);
+RB_GENERATE(g_eli_key_tree, g_eli_key, gek_link, g_eli_key_cmp);
+
+static void
+g_eli_key_fill(struct g_eli_softc *sc, struct g_eli_key *key, uint64_t keyno)
+{
+ struct {
+ char magic[4];
+ uint8_t keyno[8];
+ } __packed hmacdata;
+
+ bcopy("ekey", hmacdata.magic, 4);
+ le64enc(hmacdata.keyno, keyno);
+ g_eli_crypto_hmac(sc->sc_mkey, G_ELI_MAXKEYLEN, (uint8_t *)&hmacdata,
+ sizeof(hmacdata), key->gek_key, 0);
+ key->gek_keyno = keyno;
+ key->gek_count = 0;
+}
+
+static struct g_eli_key *
+g_eli_key_allocate(struct g_eli_softc *sc, uint64_t keyno)
+{
+ struct g_eli_key *key, *ekey, keysearch;
+
+ mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
+ mtx_unlock(&sc->sc_ekeys_lock);
+
+ key = malloc(sizeof(*key), M_ELI, M_WAITOK);
+ g_eli_key_fill(sc, key, keyno);
+
+ mtx_lock(&sc->sc_ekeys_lock);
+ /*
+ * Recheck if the key wasn't added while we weren't holding the lock.
+ */
+ keysearch.gek_keyno = keyno;
+ ekey = RB_FIND(g_eli_key_tree, &sc->sc_ekeys_tree, &keysearch);
+ if (ekey != NULL) {
+ bzero(key, sizeof(*key));
+ key = ekey;
+ TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
+ } else {
+ RB_INSERT(g_eli_key_tree, &sc->sc_ekeys_tree, key);
+ sc->sc_ekeys_allocated++;
+ }
+ TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
+
+ return (key);
+}
+
+static struct g_eli_key *
+g_eli_key_find_last(struct g_eli_softc *sc)
+{
+ struct g_eli_key *key;
+
+ mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
+
+ TAILQ_FOREACH(key, &sc->sc_ekeys_queue, gek_next) {
+ if (key->gek_count == 0)
+ break;
+ }
+
+ return (key);
+}
+
+static void
+g_eli_key_replace(struct g_eli_softc *sc, struct g_eli_key *key, uint64_t keyno)
+{
+
+ mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
+
+ RB_REMOVE(g_eli_key_tree, &sc->sc_ekeys_tree, key);
+ TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
+
+ KASSERT(key->gek_count == 0, ("gek_count=%d", key->gek_count));
+
+ g_eli_key_fill(sc, key, keyno);
+
+ RB_INSERT(g_eli_key_tree, &sc->sc_ekeys_tree, key);
+ TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
+}
+
+static void
+g_eli_key_remove(struct g_eli_softc *sc, struct g_eli_key *key)
+{
+
+ mtx_assert(&sc->sc_ekeys_lock, MA_OWNED);
+
+ KASSERT(key->gek_count == 0, ("gek_count=%d", key->gek_count));
+
+ RB_REMOVE(g_eli_key_tree, &sc->sc_ekeys_tree, key);
+ TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
+ sc->sc_ekeys_allocated--;
+ bzero(key, sizeof(*key));
+ free(key, M_ELI);
+}
+
+void
+g_eli_key_init(struct g_eli_softc *sc)
+{
+
+ mtx_lock(&sc->sc_ekeys_lock);
+ if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) {
+ uint8_t *mkey;
+
+ mkey = sc->sc_mkey + sizeof(sc->sc_ivkey);
+
+ sc->sc_ekeys_total = 1;
+ sc->sc_ekeys_allocated = 0;
+ if ((sc->sc_flags & G_ELI_FLAG_AUTH) == 0)
+ bcopy(mkey, sc->sc_ekey, 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_ekey, 0);
+ }
+ } else {
+ off_t mediasize;
+ size_t blocksize;
+
+ 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_ekeys_total =
+ ((mediasize - 1) >> G_ELI_KEY_SHIFT) / blocksize + 1;
+ sc->sc_ekeys_allocated = 0;
+ TAILQ_INIT(&sc->sc_ekeys_queue);
+ RB_INIT(&sc->sc_ekeys_tree);
+ }
+ mtx_unlock(&sc->sc_ekeys_lock);
+}
+
+void
+g_eli_key_destroy(struct g_eli_softc *sc)
+{
+
+ mtx_lock(&sc->sc_ekeys_lock);
+ if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0) {
+ bzero(sc->sc_ekey, sizeof(sc->sc_ekey));
+ } else {
+ struct g_eli_key *key;
+
+ while ((key = TAILQ_FIRST(&sc->sc_ekeys_queue)) != NULL)
+ g_eli_key_remove(sc, key);
+ TAILQ_INIT(&sc->sc_ekeys_queue);
+ RB_INIT(&sc->sc_ekeys_tree);
+ }
+ mtx_unlock(&sc->sc_ekeys_lock);
+}
+
+/*
+ * 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_key_hold(struct g_eli_softc *sc, off_t offset, size_t blocksize)
+{
+ struct g_eli_key *key, keysearch;
+ uint64_t keyno;
+
+ if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0)
+ return (sc->sc_ekey);
+
+ KASSERT(sc->sc_ekeys_total > 1, ("%s: sc_ekeys_total=%ju", __func__,
+ (uintmax_t)sc->sc_ekeys_total));
+ KASSERT((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0,
+ ("%s: SINGLE_KEY flag set, but sc_ekeys_total=%ju", __func__,
+ (uintmax_t)sc->sc_ekeys_total));
+
+ /* We switch key every 2^G_ELI_KEY_SHIFT blocks. */
+ keyno = (offset >> G_ELI_KEY_SHIFT) / blocksize;
+
+ KASSERT(keyno < sc->sc_ekeys_total,
+ ("%s: keyno=%ju >= sc_ekeys_total=%ju",
+ __func__, (uintmax_t)keyno, (uintmax_t)sc->sc_ekeys_total));
+
+ keysearch.gek_keyno = keyno;
+
+ mtx_lock(&sc->sc_ekeys_lock);
+ key = RB_FIND(g_eli_key_tree, &sc->sc_ekeys_tree, &keysearch);
+ if (key != NULL) {
+ g_eli_key_cache_hits++;
+ TAILQ_REMOVE(&sc->sc_ekeys_queue, key, gek_next);
+ TAILQ_INSERT_TAIL(&sc->sc_ekeys_queue, key, gek_next);
+ } else {
+ /*
+ * No key in cache, find the least recently unreferenced key
+ * or allocate one if we haven't reached our limit yet.
+ */
+ if (sc->sc_ekeys_allocated < g_eli_key_cache_limit) {
+ key = g_eli_key_allocate(sc, keyno);
+ } else {
+ g_eli_key_cache_misses++;
+ key = g_eli_key_find_last(sc);
+ if (key != NULL) {
+ g_eli_key_replace(sc, key, keyno);
+ } else {
+ /* All keys are referenced? Allocate one. */
+ key = g_eli_key_allocate(sc, keyno);
+ }
+ }
+ }
+ key->gek_count++;
+ mtx_unlock(&sc->sc_ekeys_lock);
+
+ return (key->gek_key);
+}
+
+void
+g_eli_key_drop(struct g_eli_softc *sc, uint8_t *rawkey)
+{
+ struct g_eli_key *key = (struct g_eli_key *)rawkey;
+
+ if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) != 0)
+ return;
+
+ mtx_lock(&sc->sc_ekeys_lock);
+ KASSERT(key->gek_count > 0, ("key->gek_count=%d", key->gek_count));
+ key->gek_count--;
+ while (sc->sc_ekeys_allocated > g_eli_key_cache_limit) {
+ key = g_eli_key_find_last(sc);
+ if (key == NULL)
+ break;
+ g_eli_key_remove(sc, key);
+ }
+ mtx_unlock(&sc->sc_ekeys_lock);
+}
diff --git a/sys/geom/eli/g_eli_privacy.c b/sys/geom/eli/g_eli_privacy.c
index ee133c6..cad3881 100644
--- a/sys/geom/eli/g_eli_privacy.c
+++ b/sys/geom/eli/g_eli_privacy.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * Copyright (c) 2005-2011 Pawel Jakub Dawidek <pawel@dawidek.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -87,6 +87,8 @@ g_eli_crypto_read_done(struct cryptop *crp)
if (bp->bio_error == 0)
bp->bio_error = crp->crp_etype;
}
+ sc = bp->bio_to->geom->softc;
+ g_eli_key_drop(sc, crp->crp_desc->crd_key);
/*
* Do we have all sectors already?
*/
@@ -102,7 +104,6 @@ g_eli_crypto_read_done(struct cryptop *crp)
/*
* Read is finished, send it up.
*/
- sc = bp->bio_to->geom->softc;
g_io_deliver(bp, bp->bio_error);
atomic_subtract_int(&sc->sc_inflight, 1);
return (0);
@@ -136,6 +137,9 @@ g_eli_crypto_write_done(struct cryptop *crp)
if (bp->bio_error == 0)
bp->bio_error = crp->crp_etype;
}
+ gp = bp->bio_to->geom;
+ sc = gp->softc;
+ g_eli_key_drop(sc, crp->crp_desc->crd_key);
/*
* All sectors are already encrypted?
*/
@@ -145,14 +149,12 @@ g_eli_crypto_write_done(struct cryptop *crp)
bp->bio_children = 1;
cbp = bp->bio_driver1;
bp->bio_driver1 = NULL;
- gp = bp->bio_to->geom;
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);
- sc = gp->softc;
g_io_deliver(bp, bp->bio_error);
atomic_subtract_int(&sc->sc_inflight, 1);
return (0);
@@ -307,12 +309,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)
+ if ((sc->sc_flags & G_ELI_FLAG_SINGLE_KEY) == 0)
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 = g_eli_crypto_key(sc, dstoff, secsize);
+ crd->crd_key = g_eli_key_hold(sc, dstoff, secsize);
crd->crd_klen = sc->sc_ekeylen;
if (sc->sc_ealgo == CRYPTO_AES_XTS)
crd->crd_klen <<= 1;
diff --git a/sys/modules/geom/geom_eli/Makefile b/sys/modules/geom/geom_eli/Makefile
index ec71a32..51d821a 100644
--- a/sys/modules/geom/geom_eli/Makefile
+++ b/sys/modules/geom/geom_eli/Makefile
@@ -8,6 +8,7 @@ SRCS+= g_eli_crypto.c
SRCS+= g_eli_ctl.c
SRCS+= g_eli_integrity.c
SRCS+= g_eli_key.c
+SRCS+= g_eli_key_cache.c
SRCS+= g_eli_privacy.c
SRCS+= pkcs5v2.c
SRCS+= vnode_if.h
OpenPOWER on IntegriCloud