MFC: Synchronize geli(8) with HEAD version.

This commit is contained in:
pjd 2006-09-04 15:26:05 +00:00
parent 9cb7cb37ab
commit 6fb52e4447
9 changed files with 618 additions and 399 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd February 11, 2006
.Dd August 9, 2006
.Dt GELI 8
.Os
.Sh NAME
@ -52,7 +52,8 @@ utility:
.Nm
.Cm init
.Op Fl bPv
.Op Fl a Ar algo
.Op Fl a Ar aalgo
.Op Fl e Ar ealgo
.Op Fl i Ar iterations
.Op Fl K Ar newkeyfile
.Op Fl l Ar keylen
@ -63,7 +64,7 @@ utility:
.Cm init
.Nm
.Cm attach
.Op Fl dpv
.Op Fl dprv
.Op Fl k Ar keyfile
.Ar prov
.Nm
@ -76,7 +77,8 @@ utility:
.Nm
.Cm onetime
.Op Fl d
.Op Fl a Ar algo
.Op Fl a Ar aalgo
.Op Fl e Ar ealgo
.Op Fl l Ar keylen
.Op Fl s Ar sectorsize
.Ar prov ...
@ -144,6 +146,16 @@ Supports many cryptographic algorithms (currently
and
.Nm 3DES ) .
.It
Can optionally perform data authentication (integrity verification) utilizing
one of the following algorithms:
.Nm HMAC/MD5 ,
.Nm HMAC/SHA1 ,
.Nm HMAC/RIPEMD160 ,
.Nm HMAC/SHA256 ,
.Nm HMAC/SHA384
or
.Nm HMAC/SHA512 .
.It
Can create a key from a couple of components (user entered passphrase, random
bits from a file, etc.).
.It
@ -151,7 +163,7 @@ Allows to encrypt the root partition - the user will be asked for the
passphrase before the root file system is mounted.
.It
The passphrase of the user is strengthened with:
.Rs
.Rs
.%A B. Kaliski
.%T "PKCS #5: Password-Based Cryptography Specification, Version 2.0."
.%R RFC
@ -163,7 +175,7 @@ Allows to use two independent keys (e.g.
and
.Qq "company key" ) .
.It
It is fast -
It is fast -
.Nm
performs simple sector-to-sector encryption.
.It
@ -177,6 +189,8 @@ the file systems).
.It
Allows to attach a provider with a random, one-time key - useful for swap
partitions and temporary file systems.
.It
Allows to verify data integrity (data authentication).
.El
.Pp
The first argument to
@ -189,8 +203,23 @@ Here you can set up the cryptographic algorithm to use, key length, etc.
The last provider's sector is used to store metadata.
.Pp
Additional options include:
.Bl -tag -width ".Fl a Ar algo"
.It Fl a Ar algo
.Bl -tag -width ".Fl a Ar aalgo"
.It Fl a Ar aalgo
Enable data integrity verification (authentication) using the given algorithm.
This will reduce size of available storage and also reduce speed.
For example, when using 4096 bytes sector and
.Nm HMAC/SHA256
algorithm, 89% of the original provider storage will be available for use.
Currently supported algorithms are:
.Nm HMAC/MD5 ,
.Nm HMAC/SHA1 ,
.Nm HMAC/RIPEMD160 ,
.Nm HMAC/SHA256 ,
.Nm HMAC/SHA384
and
.Nm HMAC/SHA512 .
If the option is not given, there will be no authentication, only encryption.
.It Fl e Ar ealgo
Encryption algorithm to use.
Currently supported algorithms are:
.Nm AES ,
@ -259,6 +288,9 @@ Probably a better choice is the
option for the
.Cm detach
subcommand.
.It Fl r
Attach read-only provider.
It will not be opened for writing.
.It Fl k Ar keyfile
Specifies a file which contains part of the key.
For more information see the description of the
@ -289,8 +321,13 @@ Attach the given providers with random, one-time keys.
The command can be used to encrypt swap partitions or temporary file systems.
.Pp
Additional options include:
.Bl -tag -width ".Fl a Ar algo"
.It Fl a Ar algo
.Bl -tag -width ".Fl a Ar aalgo"
.It Fl a Ar aalgo
Enable data integrity verification (authentication).
For more information, see the description of the
.Cm init
subcommand.
.It Fl e Ar ealgo
Encryption algorithm to use.
For more information, see the description of the
.Cm init
@ -415,6 +452,8 @@ variables can be used to control the behavior of the
.Nm ELI
GEOM class.
The default value is shown next to each variable.
All variables can also be set in
.Pa /boot/loader.conf .
.Bl -tag -width indent
.It Va kern.geom.eli.debug : No 0
Debug level of the
@ -424,8 +463,6 @@ This can be set to a number between 0 and 3 inclusive.
If set to 0, minimal debug information is printed.
If set to 3, the
maximum amount of debug information is printed.
This variable could be set in
.Pa /boot/loader.conf .
.It Va kern.geom.eli.tries : No 3
Number of times a user is asked for the passphrase.
This is only used for providers which should be attached on boot
@ -451,8 +488,11 @@ cryptography.
Its purpose is to increase performance on SMP systems.
If hardware acceleration is available, only one thread will be started.
If set to 0, CPU-bound thread will be started for every active CPU.
This variable could be set in
.Pa /boot/loader.conf .
.It Va kern.geom.eli.batch : No 0
When set to 1, can speed-up crypto operations by using batching.
Batching allows to reduce number of interrupts by responding on a group of
crypto requests with one interrupt.
The crypto card and the driver has to support this feature.
.El
.Sh EXIT STATUS
Exit status is 0 on success, and 1 if the command fails.
@ -508,7 +548,7 @@ Reenter new passphrase:
Encrypted swap partition setup:
.Bd -literal -offset indent
# dd if=/dev/random of=/dev/ad0s1b bs=1m
# geli onetime -d -a 3des ad0s1b
# geli onetime -d -e 3des ad0s1b
# swapon /dev/ad0s1b.eli
.Ed
.Pp
@ -546,6 +586,38 @@ geli_da1s3a_keyfile0_load="YES"
geli_da1s3a_keyfile0_type="da1s3a:geli_keyfile0"
geli_da1s3a_keyfile0_name="/boot/keys/da1s3a.key"
.Ed
.Pp
Not only configure encryption, but also data integrity verification using
.Nm HMAC/SHA256 .
.Bd -literal -offset indent
# geli init -a hmac/sha256 -s 4096 /dev/da0
Enter new passphrase:
Reenter new passphrase:
# geli attach /dev/da0
Enter passphrase:
# dd if=/dev/random of=/dev/da0.eli bs=1m
# newfs /dev/da0.eli
# mount /dev/da0.eli /mnt/secret
.Ed
.Sh DATA AUTHENTICATION
.Nm
can verify data integrity when an authentication algorithm is specified.
When data corruption/modification is detected,
.Nm
will not return any data, but instead will return an error
.Pq Er EINVAL .
The offset and size of the corrupted data will be printed on the console.
It is important to know against which attacks
.Nm
provides protection for your data.
If data is modified or copied from one place on the disk
to another,
.Nm
should be able to detect such a modification.
If an attacker can remember the encrypted data, modify them and write them
back to the same place, the modification will not be detected.
.Nm
will not protect your data against replay attacks.
.Sh SEE ALSO
.Xr crypto 4 ,
.Xr gbde 4 ,

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* Copyright (c) 2004-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -54,7 +54,8 @@ __FBSDID("$FreeBSD$");
uint32_t lib_version = G_LIB_VERSION;
uint32_t version = G_ELI_VERSION;
static char algo[] = "aes";
static char aalgo[] = "none";
static char ealgo[] = "aes";
static intmax_t keylen = 0;
static intmax_t keyno = -1;
static intmax_t iterations = -1;
@ -75,12 +76,12 @@ static void eli_dump(struct gctl_req *req);
/*
* Available commands:
*
* init [-bhPv] [-a algo] [-i iterations] [-l keylen] [-K newkeyfile] prov
* init [-bhPv] [-a aalgo] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] prov
* label - alias for 'init'
* attach [-dpv] [-k keyfile] prov
* attach [-dprv] [-k keyfile] prov
* detach [-fl] prov ...
* stop - alias for 'detach'
* onetime [-d] [-a algo] [-l keylen] prov ...
* onetime [-d] [-a aalgo] [-e ealgo] [-l keylen] prov ...
* setkey [-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov
* delkey [-afv] [-n keyno] prov
* kill [-av] [prov ...]
@ -92,8 +93,9 @@ static void eli_dump(struct gctl_req *req);
struct g_command class_commands[] = {
{ "init", G_FLAG_VERBOSE, eli_main,
{
{ 'a', "algo", algo, G_TYPE_STRING },
{ 'a', "aalgo", aalgo, G_TYPE_STRING },
{ 'b', "boot", NULL, G_TYPE_NONE },
{ 'e', "ealgo", ealgo, G_TYPE_STRING },
{ 'i', "iterations", &iterations, G_TYPE_NUMBER },
{ 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
{ 'l', "keylen", &keylen, G_TYPE_NUMBER },
@ -101,12 +103,13 @@ struct g_command class_commands[] = {
{ 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
G_OPT_SENTINEL
},
"[-bPv] [-a algo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov"
"[-bPv] [-a aalgo] [-e ealgo] [-i iterations] [-l keylen] [-K newkeyfile] [-s sectorsize] prov"
},
{ "label", G_FLAG_VERBOSE, eli_main,
{
{ 'a', "algo", algo, G_TYPE_STRING },
{ 'a', "aalgo", aalgo, G_TYPE_STRING },
{ 'b', "boot", NULL, G_TYPE_NONE },
{ 'e', "ealgo", ealgo, G_TYPE_STRING },
{ 'i', "iterations", &iterations, G_TYPE_NUMBER },
{ 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
{ 'l', "keylen", &keylen, G_TYPE_NUMBER },
@ -121,9 +124,10 @@ struct g_command class_commands[] = {
{ 'd', "detach", NULL, G_TYPE_NONE },
{ 'k', "keyfile", keyfile, G_TYPE_STRING },
{ 'p', "nopassphrase", NULL, G_TYPE_NONE },
{ 'r', "readonly", NULL, G_TYPE_NONE },
G_OPT_SENTINEL
},
"[-dpv] [-k keyfile] prov"
"[-dprv] [-k keyfile] prov"
},
{ "detach", 0, NULL,
{
@ -143,13 +147,14 @@ struct g_command class_commands[] = {
},
{ "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL,
{
{ 'a', "algo", algo, G_TYPE_STRING },
{ 'a', "aalgo", aalgo, G_TYPE_STRING },
{ 'd', "detach", NULL, G_TYPE_NONE },
{ 'e', "ealgo", ealgo, G_TYPE_STRING },
{ 'l', "keylen", &keylen, G_TYPE_NUMBER },
{ 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
G_OPT_SENTINEL
},
"[-d] [-a algo] [-l keylen] [-s sectorsize] prov ..."
"[-d] [-a aalgo] [-e ealgo] [-l keylen] [-s sectorsize] prov ..."
},
{ "setkey", G_FLAG_VERBOSE, eli_main,
{
@ -389,7 +394,7 @@ eli_genkey(struct gctl_req *req, struct g_eli_metadata *md, unsigned char *key,
}
}
/*
* If md_iterations is equal to 0, user don't want PKCS5v2.
* If md_iterations is equal to 0, user don't want PKCS#5v2.
*/
if (md->md_iterations == 0) {
g_eli_crypto_hmac_update(&ctx, md->md_salt,
@ -523,16 +528,44 @@ eli_init(struct gctl_req *req)
md.md_flags = 0;
if (gctl_get_int(req, "boot"))
md.md_flags |= G_ELI_FLAG_BOOT;
str = gctl_get_ascii(req, "algo");
md.md_algo = g_eli_str2algo(str);
if (md.md_algo < CRYPTO_ALGORITHM_MIN ||
md.md_algo > CRYPTO_ALGORITHM_MAX) {
gctl_error(req, "Invalid encryption algorithm.");
return;
md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
str = gctl_get_ascii(req, "aalgo");
if (strcmp(str, "none") != 0) {
md.md_aalgo = g_eli_str2aalgo(str);
if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
md.md_flags |= G_ELI_FLAG_AUTH;
} else {
/*
* For backward compatibility, check if the -a option
* was used to provide encryption algorithm.
*/
md.md_ealgo = g_eli_str2ealgo(str);
if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
gctl_error(req,
"Invalid authentication algorithm.");
return;
} else {
fprintf(stderr, "warning: The -e option, not "
"the -a option is now used to specify "
"encryption algorithm to use.\n");
}
}
}
if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
str = gctl_get_ascii(req, "ealgo");
md.md_ealgo = g_eli_str2ealgo(str);
if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
gctl_error(req, "Invalid encryption algorithm.");
return;
}
}
val = gctl_get_intmax(req, "keylen");
md.md_keylen = val;
md.md_keylen = g_eli_keylen(md.md_algo, md.md_keylen);
md.md_keylen = g_eli_keylen(md.md_ealgo, md.md_keylen);
if (md.md_keylen == 0) {
gctl_error(req, "Invalid key length.");
return;
@ -579,7 +612,7 @@ eli_init(struct gctl_req *req)
}
/* Encrypt the first and the only Master Key. */
error = g_eli_mkey_encrypt(md.md_algo, key, md.md_keylen, md.md_mkeys);
error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, md.md_mkeys);
bzero(key, sizeof(key));
if (error != 0) {
bzero(&md, sizeof(md));
@ -733,7 +766,7 @@ eli_setkey_detached(struct gctl_req *req, const char *prov,
}
/* Encrypt the Master-Key with the new key. */
error = g_eli_mkey_encrypt(md->md_algo, key, md->md_keylen, mkeydst);
error = g_eli_mkey_encrypt(md->md_ealgo, key, md->md_keylen, mkeydst);
bzero(key, sizeof(key));
if (error != 0) {
bzero(md, sizeof(*md));

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -69,16 +69,20 @@ SYSCTL_UINT(_kern_geom_eli, OID_AUTO, visible_passphrase, CTLFLAG_RW,
&g_eli_visible_passphrase, 0,
"Turn on echo when entering the passphrase (for debug purposes only!!)");
u_int g_eli_overwrites = 5;
TUNABLE_INT("kern.geom.eli.overwrites", &g_eli_overwrites);
SYSCTL_UINT(_kern_geom_eli, OID_AUTO, overwrites, CTLFLAG_RW, &g_eli_overwrites,
0, "Number of times on-disk keys should be overwritten when destroying them");
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;
@ -106,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;
@ -139,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;
@ -149,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;
}
@ -163,70 +177,31 @@ g_eli_read_done(struct bio *bp)
wakeup(sc);
}
/*
* 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;
@ -235,67 +210,10 @@ 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.
@ -358,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);
@ -411,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);
@ -426,20 +349,27 @@ 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)
{
u_char hash[SHA256_DIGEST_LENGTH];
u_char off[8], hash[SHA256_DIGEST_LENGTH];
SHA256_CTX ctx;
if (!(sc->sc_flags & G_ELI_FLAG_NATIVE_BYTE_ORDER))
le64enc(off, (uint64_t)offset);
/* Copy precalculated SHA256 context for IV-Key. */
bcopy(&sc->sc_ivctx, &ctx, sizeof(ctx));
SHA256_Update(&ctx, (uint8_t *)&offset, sizeof(offset));
@ -447,110 +377,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)
@ -631,6 +457,10 @@ g_eli_access(struct g_provider *pp, int dr, int dw, int de)
sc = gp->softc;
if (dw > 0) {
if (sc->sc_flags & G_ELI_FLAG_RO) {
/* Deny write attempts. */
return (EROFS);
}
/* Someone is opening us for write, we need to remember that. */
sc->sc_flags |= G_ELI_FLAG_WOPEN;
return (0);
@ -658,7 +488,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;
@ -671,31 +501,60 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp,
gp->start = g_eli_start;
/*
* Spoiling cannot happen actually, because we keep provider open for
* writing all the time.
* writing all the time or provider is read-only.
*/
gp->spoiled = g_eli_orphan_spoil_assert;
gp->orphan = g_eli_orphan;
gp->dumpconf = g_eli_dumpconf;
/*
* If detach-on-last-close feature is not enabled, we can simply use
* g_std_access().
* If detach-on-last-close feature is not enabled and we don't operate
* on read-only provider, we can simply use g_std_access().
*/
if (md->md_flags & G_ELI_FLAG_WO_DETACH)
if (md->md_flags & (G_ELI_FLAG_WO_DETACH | G_ELI_FLAG_RO))
gp->access = g_eli_access;
else
gp->access = g_std_access;
gp->dumpconf = g_eli_dumpconf;
sc->sc_crypto = G_ELI_CRYPTO_SW;
sc->sc_flags = md->md_flags;
sc->sc_algo = md->md_algo;
/* Backward compatibility. */
if (md->md_version < 2)
sc->sc_flags |= G_ELI_FLAG_NATIVE_BYTE_ORDER;
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.
@ -728,8 +587,13 @@ g_eli_create(struct gctl_req *req, struct g_class *mp, struct g_provider *bpp,
* Keep provider open all the time, so we can run critical tasks,
* like Master Keys deletion, without wondering if we can open
* provider or not.
* We don't open provider for writing only when user requested read-only
* access.
*/
error = g_access(cp, 1, 1, 1);
if (sc->sc_flags & G_ELI_FLAG_RO)
error = g_access(cp, 1, 0, 1);
else
error = g_access(cp, 1, 1, 1);
if (error != 0) {
if (req != NULL) {
gctl_error(req, "Cannot access %s (error=%d).",
@ -743,10 +607,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)
@ -766,12 +637,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) {
@ -810,14 +681,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);
@ -995,7 +874,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);
@ -1117,7 +996,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 \
@ -1125,17 +1004,20 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
sbuf_printf(sb, name); \
} \
} while (0)
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");
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");
ADD_FLAG(G_ELI_FLAG_RO, "READ-ONLY");
#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);
}
@ -1152,9 +1034,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);

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -54,28 +54,43 @@
/*
* Version history:
* 0 - Initial version number.
* 1 - Added data authentication support (md_aalgo field and
* G_ELI_FLAG_AUTH flag).
* 2 - Added G_ELI_FLAG_READONLY.
* - IV is generated from offset converted to little-endian
* (flag G_ELI_FLAG_NATIVE_BYTE_ORDER will be set for older versions).
*/
#define G_ELI_VERSION 0
#define G_ELI_VERSION 2
/* ON DISK FLAGS. */
/* Use random, onetime keys. */
#define G_ELI_FLAG_ONETIME 0x00000001
#define G_ELI_FLAG_ONETIME 0x00000001
/* Ask for the passphrase from the kernel, before mounting root. */
#define G_ELI_FLAG_BOOT 0x00000002
#define G_ELI_FLAG_BOOT 0x00000002
/* Detach on last close, if we were open for writing. */
#define G_ELI_FLAG_WO_DETACH 0x00000004
#define G_ELI_FLAG_WO_DETACH 0x00000004
/* Detach on last close. */
#define G_ELI_FLAG_RW_DETACH 0x00000008
#define G_ELI_FLAG_RW_DETACH 0x00000008
/* Provide data authentication. */
#define G_ELI_FLAG_AUTH 0x00000010
/* Provider is read-only, we should deny all write attempts. */
#define G_ELI_FLAG_RO 0x00000020
/* RUNTIME FLAGS. */
/* Provider was open for writing. */
#define G_ELI_FLAG_WOPEN 0x00010000
#define G_ELI_FLAG_WOPEN 0x00010000
/* Destroy device. */
#define G_ELI_FLAG_DESTROY 0x00020000
#define G_ELI_FLAG_DESTROY 0x00020000
/* Provider uses native byte-order for IV generation. */
#define G_ELI_FLAG_NATIVE_BYTE_ORDER 0x00040000
#define SHA512_MDLEN 64
#define G_ELI_AUTH_SECKEYLEN SHA256_DIGEST_LENGTH
#define G_ELI_MAXMKEYS 2
#define G_ELI_MAXKEYLEN 64
#define G_ELI_USERKEYLEN G_ELI_MAXKEYLEN
#define G_ELI_DATAKEYLEN G_ELI_MAXKEYLEN
#define G_ELI_AUTHKEYLEN G_ELI_MAXKEYLEN
#define G_ELI_IVKEYLEN G_ELI_MAXKEYLEN
#define G_ELI_SALTLEN 64
#define G_ELI_DATAIVKEYLEN (G_ELI_DATAKEYLEN + G_ELI_IVKEYLEN)
@ -85,6 +100,7 @@
#ifdef _KERNEL
extern u_int g_eli_debug;
extern u_int g_eli_overwrites;
extern u_int g_eli_batch;
#define G_ELI_CRYPTO_HW 1
#define G_ELI_CRYPTO_SW 2
@ -123,13 +139,21 @@ struct g_eli_worker {
struct g_eli_softc {
struct g_geom *sc_geom;
u_int sc_crypto;
uint8_t sc_datakey[G_ELI_DATAKEYLEN];
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;
u_int sc_algo;
u_int sc_keylen;
int sc_nkey;
uint32_t sc_flags;
u_int sc_bytes_per_sector;
u_int sc_data_per_sector;
/* Only for software cryptography. */
struct bio_queue_head sc_queue;
@ -143,8 +167,9 @@ struct g_eli_metadata {
char md_magic[16]; /* Magic value. */
uint32_t md_version; /* Version number. */
uint32_t md_flags; /* Additional flags. */
uint16_t md_algo; /* Encryption algorithm. */
uint16_t md_ealgo; /* Encryption algorithm. */
uint16_t md_keylen; /* Key length. */
uint16_t md_aalgo; /* Authentication algorithm. */
uint64_t md_provsize; /* Provider's size. */
uint32_t md_sectorsize; /* Sector size. */
uint8_t md_keys; /* Available keys. */
@ -165,8 +190,9 @@ eli_metadata_encode(struct g_eli_metadata *md, u_char *data)
bcopy(md->md_magic, p, sizeof(md->md_magic)); p += sizeof(md->md_magic);
le32enc(p, md->md_version); p += sizeof(md->md_version);
le32enc(p, md->md_flags); p += sizeof(md->md_flags);
le16enc(p, md->md_algo); p += sizeof(md->md_algo);
le16enc(p, md->md_ealgo); p += sizeof(md->md_ealgo);
le16enc(p, md->md_keylen); p += sizeof(md->md_keylen);
le16enc(p, md->md_aalgo); p += sizeof(md->md_aalgo);
le64enc(p, md->md_provsize); p += sizeof(md->md_provsize);
le32enc(p, md->md_sectorsize); p += sizeof(md->md_sectorsize);
*p = md->md_keys; p += sizeof(md->md_keys);
@ -186,7 +212,7 @@ eli_metadata_decode_v0(const u_char *data, struct g_eli_metadata *md)
p = data + sizeof(md->md_magic) + sizeof(md->md_version);
md->md_flags = le32dec(p); p += sizeof(md->md_flags);
md->md_algo = le16dec(p); p += sizeof(md->md_algo);
md->md_ealgo = le16dec(p); p += sizeof(md->md_ealgo);
md->md_keylen = le16dec(p); p += sizeof(md->md_keylen);
md->md_provsize = le64dec(p); p += sizeof(md->md_provsize);
md->md_sectorsize = le32dec(p); p += sizeof(md->md_sectorsize);
@ -202,6 +228,30 @@ eli_metadata_decode_v0(const u_char *data, struct g_eli_metadata *md)
return (0);
}
static __inline int
eli_metadata_decode_v1v2(const u_char *data, struct g_eli_metadata *md)
{
MD5_CTX ctx;
const u_char *p;
p = data + sizeof(md->md_magic) + sizeof(md->md_version);
md->md_flags = le32dec(p); p += sizeof(md->md_flags);
md->md_ealgo = le16dec(p); p += sizeof(md->md_ealgo);
md->md_keylen = le16dec(p); p += sizeof(md->md_keylen);
md->md_aalgo = le16dec(p); p += sizeof(md->md_aalgo);
md->md_provsize = le64dec(p); p += sizeof(md->md_provsize);
md->md_sectorsize = le32dec(p); p += sizeof(md->md_sectorsize);
md->md_keys = *p; p += sizeof(md->md_keys);
md->md_iterations = le32dec(p); p += sizeof(md->md_iterations);
bcopy(p, md->md_salt, sizeof(md->md_salt)); p += sizeof(md->md_salt);
bcopy(p, md->md_mkeys, sizeof(md->md_mkeys)); p += sizeof(md->md_mkeys);
MD5Init(&ctx);
MD5Update(&ctx, data, p - data);
MD5Final(md->md_hash, &ctx);
if (bcmp(md->md_hash, p, 16) != 0)
return (EINVAL);
return (0);
}
static __inline int
eli_metadata_decode(const u_char *data, struct g_eli_metadata *md)
{
int error;
@ -212,6 +262,10 @@ eli_metadata_decode(const u_char *data, struct g_eli_metadata *md)
case 0:
error = eli_metadata_decode_v0(data, md);
break;
case 1:
case 2:
error = eli_metadata_decode_v1v2(data, md);
break;
default:
error = EINVAL;
break;
@ -221,7 +275,7 @@ eli_metadata_decode(const u_char *data, struct g_eli_metadata *md)
#endif /* !_OpenSSL */
static __inline u_int
g_eli_str2algo(const char *name)
g_eli_str2ealgo(const char *name)
{
if (strcasecmp("null", name) == 0)
@ -235,6 +289,25 @@ g_eli_str2algo(const char *name)
return (CRYPTO_ALGORITHM_MIN - 1);
}
static __inline u_int
g_eli_str2aalgo(const char *name)
{
if (strcasecmp("hmac/md5", name) == 0)
return (CRYPTO_MD5_HMAC);
else if (strcasecmp("hmac/sha1", name) == 0)
return (CRYPTO_SHA1_HMAC);
else if (strcasecmp("hmac/ripemd160", name) == 0)
return (CRYPTO_RIPEMD160_HMAC);
else if (strcasecmp("hmac/sha256", name) == 0)
return (CRYPTO_SHA2_256_HMAC);
else if (strcasecmp("hmac/sha384", name) == 0)
return (CRYPTO_SHA2_384_HMAC);
else if (strcasecmp("hmac/sha512", name) == 0)
return (CRYPTO_SHA2_512_HMAC);
return (CRYPTO_ALGORITHM_MIN - 1);
}
static __inline const char *
g_eli_algo2str(u_int algo)
{
@ -243,11 +316,23 @@ g_eli_algo2str(u_int algo)
case CRYPTO_NULL_CBC:
return ("NULL");
case CRYPTO_AES_CBC:
return ("AES");
return ("AES-CBC");
case CRYPTO_BLF_CBC:
return ("Blowfish");
return ("Blowfish-CBC");
case CRYPTO_3DES_CBC:
return ("3DES");
return ("3DES-CBC");
case CRYPTO_MD5_HMAC:
return ("HMAC/MD5");
case CRYPTO_SHA1_HMAC:
return ("HMAC/SHA1");
case CRYPTO_RIPEMD160_HMAC:
return ("HMAC/RIPEMD160");
case CRYPTO_SHA2_256_HMAC:
return ("HMAC/SHA256");
case CRYPTO_SHA2_384_HMAC:
return ("HMAC/SHA384");
case CRYPTO_SHA2_512_HMAC:
return ("HMAC/SHA512");
}
return ("unknown");
}
@ -262,8 +347,10 @@ eli_metadata_dump(const struct g_eli_metadata *md)
printf(" magic: %s\n", md->md_magic);
printf(" version: %u\n", (u_int)md->md_version);
printf(" flags: 0x%x\n", (u_int)md->md_flags);
printf(" algo: %s\n", g_eli_algo2str(md->md_algo));
printf(" ealgo: %s\n", g_eli_algo2str(md->md_ealgo));
printf(" keylen: %u\n", (u_int)md->md_keylen);
if (md->md_flags & G_ELI_FLAG_AUTH)
printf(" aalgo: %s\n", g_eli_algo2str(md->md_aalgo));
printf(" provsize: %ju\n", (uintmax_t)md->md_provsize);
printf("sectorsize: %u\n", (u_int)md->md_sectorsize);
printf(" keys: 0x%02x\n", (u_int)md->md_keys);
@ -329,6 +416,27 @@ g_eli_keylen(u_int algo, u_int keylen)
}
}
static __inline u_int
g_eli_hashlen(u_int algo)
{
switch (algo) {
case CRYPTO_MD5_HMAC:
return (16);
case CRYPTO_SHA1_HMAC:
return (20);
case CRYPTO_RIPEMD160_HMAC:
return (20);
case CRYPTO_SHA2_256_HMAC:
return (32);
case CRYPTO_SHA2_384_HMAC:
return (48);
case CRYPTO_SHA2_512_HMAC:
return (64);
}
return (0);
}
#ifdef _KERNEL
int g_eli_read_metadata(struct g_class *mp, struct g_provider *pp,
struct g_eli_metadata *md);
@ -339,6 +447,17 @@ int g_eli_destroy(struct g_eli_softc *sc, boolean_t force);
int g_eli_access(struct g_provider *pp, int dr, int dw, int de);
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);
void g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv,
size_t size);
void g_eli_crypto_run(struct g_eli_worker *wr, struct bio *bp);
void g_eli_auth_read(struct g_eli_softc *sc, struct bio *bp);
void g_eli_auth_run(struct g_eli_worker *wr, struct bio *bp);
#endif
void g_eli_mkey_hmac(unsigned char *mkey, const unsigned char *key);
@ -346,6 +465,9 @@ int g_eli_mkey_decrypt(const struct g_eli_metadata *md,
const unsigned char *key, unsigned char *mkey, unsigned *nkeyp);
int g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen,
unsigned char *mkey);
#ifdef _KERNEL
void g_eli_mkey_propagate(struct g_eli_softc *sc, const unsigned char *mkey);
#endif
int g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize,
const u_char *key, size_t keysize);

View File

@ -57,7 +57,7 @@ g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp)
struct g_provider *pp;
const char *name;
u_char *key, mkey[G_ELI_DATAIVKEYLEN];
int *nargs, *detach;
int *nargs, *detach, *readonly;
int keysize, error;
u_int nkey;
@ -79,6 +79,12 @@ g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp)
return;
}
readonly = gctl_get_paraml(req, "readonly", sizeof(*readonly));
if (readonly == NULL) {
gctl_error(req, "No '%s' argument.", "readonly");
return;
}
name = gctl_get_asciiparam(req, "arg0");
if (name == NULL) {
gctl_error(req, "No 'arg%u' argument.", 0);
@ -124,8 +130,16 @@ g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp)
}
G_ELI_DEBUG(1, "Using Master Key %u for %s.", nkey, pp->name);
if (*detach && *readonly) {
bzero(&md, sizeof(md));
gctl_error(req, "Options -d and -r are mutually exclusive.",
pp->name, error);
return;
}
if (*detach)
md.md_flags |= G_ELI_FLAG_WO_DETACH;
if (*readonly)
md.md_flags |= G_ELI_FLAG_RO;
g_eli_create(req, mp, pp, &md, mkey, nkey);
bzero(mkey, sizeof(mkey));
bzero(&md, sizeof(md));
@ -250,16 +264,49 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp)
if (*detach)
md.md_flags |= G_ELI_FLAG_WO_DETACH;
name = gctl_get_asciiparam(req, "algo");
md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
name = gctl_get_asciiparam(req, "aalgo");
if (name == NULL) {
gctl_error(req, "No '%s' argument.", "algo");
gctl_error(req, "No '%s' argument.", "aalgo");
return;
}
md.md_algo = g_eli_str2algo(name);
if (md.md_algo < CRYPTO_ALGORITHM_MIN ||
md.md_algo > CRYPTO_ALGORITHM_MAX) {
gctl_error(req, "Invalid '%s' argument.", "algo");
return;
if (strcmp(name, "none") != 0) {
md.md_aalgo = g_eli_str2aalgo(name);
if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
md.md_flags |= G_ELI_FLAG_AUTH;
} else {
/*
* For backward compatibility, check if the -a option
* was used to provide encryption algorithm.
*/
md.md_ealgo = g_eli_str2ealgo(name);
if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
gctl_error(req,
"Invalid authentication algorithm.");
return;
} else {
gctl_error(req, "warning: The -e option, not "
"the -a option is now used to specify "
"encryption algorithm to use.");
}
}
}
if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
name = gctl_get_asciiparam(req, "ealgo");
if (name == NULL) {
gctl_error(req, "No '%s' argument.", "ealgo");
return;
}
md.md_ealgo = g_eli_str2ealgo(name);
if (md.md_ealgo < CRYPTO_ALGORITHM_MIN ||
md.md_ealgo > CRYPTO_ALGORITHM_MAX) {
gctl_error(req, "Invalid encryption algorithm.");
return;
}
}
keylen = gctl_get_paraml(req, "keylen", sizeof(*keylen));
@ -267,7 +314,7 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp)
gctl_error(req, "No '%s' argument.", "keylen");
return;
}
md.md_keylen = g_eli_keylen(md.md_algo, *keylen);
md.md_keylen = g_eli_keylen(md.md_ealgo, *keylen);
if (md.md_keylen == 0) {
gctl_error(req, "Invalid '%s' argument.", "keylen");
return;
@ -341,6 +388,10 @@ g_eli_ctl_setkey(struct gctl_req *req, struct g_class *mp)
gctl_error(req, "Provider %s is invalid.", name);
return;
}
if (sc->sc_flags & G_ELI_FLAG_RO) {
gctl_error(req, "Cannot change keys for read-only provider.");
return;
}
cp = LIST_FIRST(&sc->sc_geom->consumer);
pp = cp->provider;
@ -395,12 +446,10 @@ g_eli_ctl_setkey(struct gctl_req *req, struct g_class *mp)
mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
md.md_keys |= (1 << nkey);
bcopy(sc->sc_ivkey, mkeydst, sizeof(sc->sc_ivkey));
bcopy(sc->sc_datakey, mkeydst + sizeof(sc->sc_ivkey),
sizeof(sc->sc_datakey));
bcopy(sc->sc_mkey, mkeydst, sizeof(sc->sc_mkey));
/* Encrypt Master Key with the new key. */
error = g_eli_mkey_encrypt(md.md_algo, key, md.md_keylen, mkeydst);
error = g_eli_mkey_encrypt(md.md_ealgo, key, md.md_keylen, mkeydst);
bzero(key, sizeof(key));
if (error != 0) {
bzero(&md, sizeof(md));
@ -452,6 +501,10 @@ g_eli_ctl_delkey(struct gctl_req *req, struct g_class *mp)
gctl_error(req, "Provider %s is invalid.", name);
return;
}
if (sc->sc_flags & G_ELI_FLAG_RO) {
gctl_error(req, "Cannot delete keys for read-only provider.");
return;
}
cp = LIST_FIRST(&sc->sc_geom->consumer);
pp = cp->provider;
@ -534,9 +587,7 @@ g_eli_kill_one(struct g_eli_softc *sc)
{
struct g_provider *pp;
struct g_consumer *cp;
u_char *sector;
int err, error = 0;
u_int i;
int error = 0;
g_topology_assert();
@ -549,22 +600,31 @@ g_eli_kill_one(struct g_eli_softc *sc)
cp = LIST_FIRST(&sc->sc_geom->consumer);
pp = cp->provider;
sector = malloc(pp->sectorsize, M_ELI, M_WAITOK);
for (i = 0; i <= g_eli_overwrites; i++) {
if (i == g_eli_overwrites)
bzero(sector, pp->sectorsize);
else
arc4rand(sector, pp->sectorsize, 0);
err = g_write_data(cp, pp->mediasize - pp->sectorsize, sector,
pp->sectorsize);
if (err != 0) {
G_ELI_DEBUG(0, "Cannot erase metadata on %s "
"(error=%d).", pp->name, err);
if (error == 0)
error = err;
if (sc->sc_flags & G_ELI_FLAG_RO) {
G_ELI_DEBUG(0, "WARNING: Metadata won't be erased on read-only "
"provider: %s.", pp->name);
} else {
u_char *sector;
u_int i;
int err;
sector = malloc(pp->sectorsize, M_ELI, M_WAITOK);
for (i = 0; i <= g_eli_overwrites; i++) {
if (i == g_eli_overwrites)
bzero(sector, pp->sectorsize);
else
arc4rand(sector, pp->sectorsize, 0);
err = g_write_data(cp, pp->mediasize - pp->sectorsize,
sector, pp->sectorsize);
if (err != 0) {
G_ELI_DEBUG(0, "Cannot erase metadata on %s "
"(error=%d).", pp->name, err);
if (error == 0)
error = err;
}
}
free(sector, M_ELI);
}
free(sector, M_ELI);
if (error == 0)
G_ELI_DEBUG(0, "%s has been killed.", pp->name);
g_eli_destroy(sc, 1);

View File

@ -123,10 +123,10 @@ g_eli_mkey_decrypt(const struct g_eli_metadata *md, const unsigned char *key,
nkey = 0;
for (nkey = 0; nkey < G_ELI_MAXMKEYS; nkey++, mmkey += G_ELI_MKEYLEN) {
bit = (1 << nkey);
if ((md->md_keys & bit) == 0)
if (!(md->md_keys & bit))
continue;
bcopy(mmkey, tmpmkey, G_ELI_MKEYLEN);
error = g_eli_crypto_decrypt(md->md_algo, tmpmkey,
error = g_eli_crypto_decrypt(md->md_ealgo, tmpmkey,
G_ELI_MKEYLEN, enckey, md->md_keylen);
if (error != 0) {
bzero(tmpmkey, sizeof(tmpmkey));
@ -177,3 +177,33 @@ g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen,
return (error);
}
#ifdef _KERNEL
/*
* When doing encryption only, copy IV key and encryption key.
* When doing encryption and authentication, copy IV key, generate encryption
* key and generate authentication key.
*/
void
g_eli_mkey_propagate(struct g_eli_softc *sc, const unsigned char *mkey)
{
/* Remember the Master Key. */
bcopy(mkey, sc->sc_mkey, sizeof(sc->sc_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));
} 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);
}
}
#endif

View File

@ -3,7 +3,14 @@
.PATH: ${.CURDIR}/../../../geom/eli
KMOD= geom_eli
SRCS= g_eli.c g_eli_crypto.c g_eli_ctl.c g_eli_key.c pkcs5v2.c vnode_if.h
SRCS= g_eli.c
SRCS+= g_eli_crypto.c
SRCS+= g_eli_ctl.c
SRCS+= g_eli_integrity.c
SRCS+= g_eli_key.c
SRCS+= g_eli_privacy.c
SRCS+= pkcs5v2.c
SRCS+= vnode_if.h
WARNS?= 2
.include <bsd.kmod.mk>

View File

@ -4,11 +4,9 @@
base=`basename $0`
no=45
sectors=100
rnd=`mktemp /tmp/$base.XXXXXX` || exit 1
keyfile=`mktemp /tmp/$base.XXXXXX` || exit 1
mdconfig -a -t malloc -s `expr $sectors + 1` -u $no || exit 1
echo "1..36"
echo "1..180"
i=1
for cipher in aes:0 aes:128 aes:192 aes:256 \
@ -16,36 +14,43 @@ for cipher in aes:0 aes:128 aes:192 aes:256 \
blowfish:0 blowfish:128 blowfish:160 blowfish:192 blowfish:224 \
blowfish:256 blowfish:288 blowfish:320 blowfish:352 blowfish:384 \
blowfish:416 blowfish:448; do
algo=${cipher%%:*}
ealgo=${cipher%%:*}
keylen=${cipher##*:}
for secsize in 512 1024 2048 4096 8192; do
rnd=`mktemp /tmp/$base.XXXXXX` || exit 1
mdconfig -a -t malloc -s `expr $secsize \* $sectors + 512`b -u $no || exit 1
dd if=/dev/random of=${rnd} bs=512 count=${sectors} >/dev/null 2>&1
dd if=/dev/random of=${keyfile} bs=512 count=16 >/dev/null 2>&1
dd if=/dev/random of=${keyfile} bs=512 count=16 >/dev/null 2>&1
geli init -a $algo -l $keylen -P -K $keyfile md${no}
geli attach -p -k $keyfile md${no}
geli init -e $ealgo -l $keylen -P -K $keyfile -s $secsize md${no}
geli attach -p -k $keyfile md${no}
dd if=${rnd} of=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null
secs=`diskinfo /dev/md${no}.eli | awk '{print $4}'`
md_rnd=`dd if=${rnd} bs=512 count=${sectors} 2>/dev/null | md5`
md_ddev=`dd if=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null | md5`
md_edev=`dd if=/dev/md${no} bs=512 count=${sectors} 2>/dev/null | md5`
dd if=/dev/random of=${rnd} bs=${secsize} count=${secs} >/dev/null 2>&1
dd if=${rnd} of=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null
if [ ${md_rnd} = ${md_ddev} ]; then
echo "ok $i - ${cipher}"
else
echo "not ok $i - ${cipher}"
fi
i=$((i+1))
if [ ${md_rnd} != ${md_edev} ]; then
echo "ok $i - ${cipher}"
else
echo "not ok $i - ${cipher}"
fi
i=$((i+1))
md_rnd=`dd if=${rnd} bs=${secsize} count=${secs} 2>/dev/null | md5`
md_ddev=`dd if=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null | md5`
md_edev=`dd if=/dev/md${no} bs=${secsize} count=${secs} 2>/dev/null | md5`
geli detach md${no}
if [ ${md_rnd} = ${md_ddev} ]; then
echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
else
echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
fi
i=$((i+1))
if [ ${md_rnd} != ${md_edev} ]; then
echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
else
echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
fi
i=$((i+1))
geli detach md${no}
rm -f $rnd
mdconfig -d -u $no
done
done
mdconfig -d -u $no
rm -f $rnd $keyfile
rm -f $keyfile

View File

@ -4,10 +4,8 @@
base=`basename $0`
no=45
sectors=100
rnd=`mktemp /tmp/$base.XXXXXX` || exit 1
mdconfig -a -t malloc -s $sectors -u $no || exit 1
echo "1..36"
echo "1..180"
i=1
for cipher in aes:0 aes:128 aes:192 aes:256 \
@ -15,34 +13,38 @@ for cipher in aes:0 aes:128 aes:192 aes:256 \
blowfish:0 blowfish:128 blowfish:160 blowfish:192 blowfish:224 \
blowfish:256 blowfish:288 blowfish:320 blowfish:352 blowfish:384 \
blowfish:416 blowfish:448; do
algo=${cipher%%:*}
ealgo=${cipher%%:*}
keylen=${cipher##*:}
for secsize in 512 1024 2048 4096 8192; do
rnd=`mktemp /tmp/$base.XXXXXX` || exit 1
mdconfig -a -t malloc -s `expr $secsize \* $sectors`b -u $no || exit 1
dd if=/dev/random of=${rnd} bs=512 count=${sectors} >/dev/null 2>&1
geli onetime -e $ealgo -l $keylen -s $secsize md${no}
geli onetime -a $algo -l $keylen md${no}
secs=`diskinfo /dev/md${no}.eli | awk '{print $4}'`
dd if=${rnd} of=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null
dd if=/dev/random of=${rnd} bs=${secsize} count=${secs} >/dev/null 2>&1
dd if=${rnd} of=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null
md_rnd=`dd if=${rnd} bs=512 count=${sectors} 2>/dev/null | md5`
md_ddev=`dd if=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null | md5`
md_edev=`dd if=/dev/md${no} bs=512 count=${sectors} 2>/dev/null | md5`
md_rnd=`dd if=${rnd} bs=${secsize} count=${secs} 2>/dev/null | md5`
md_ddev=`dd if=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null | md5`
md_edev=`dd if=/dev/md${no} bs=${secsize} count=${secs} 2>/dev/null | md5`
if [ ${md_rnd} = ${md_ddev} ]; then
echo "ok $i - ${cipher}"
else
echo "not ok $i - ${cipher}"
fi
i=$((i+1))
if [ ${md_rnd} != ${md_edev} ]; then
echo "ok $i - ${cipher}"
else
echo "not ok $i - ${cipher}"
fi
i=$((i+1))
if [ ${md_rnd} = ${md_ddev} ]; then
echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
else
echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
fi
i=$((i+1))
if [ ${md_rnd} != ${md_edev} ]; then
echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
else
echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
fi
i=$((i+1))
geli detach md${no}
geli detach md${no}
rm -f $rnd
mdconfig -d -u $no
done
done
mdconfig -d -u $no
rm -f $rnd