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$ .\" $FreeBSD$
.\" .\"
.Dd February 11, 2006 .Dd August 9, 2006
.Dt GELI 8 .Dt GELI 8
.Os .Os
.Sh NAME .Sh NAME
@ -52,7 +52,8 @@ utility:
.Nm .Nm
.Cm init .Cm init
.Op Fl bPv .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 i Ar iterations
.Op Fl K Ar newkeyfile .Op Fl K Ar newkeyfile
.Op Fl l Ar keylen .Op Fl l Ar keylen
@ -63,7 +64,7 @@ utility:
.Cm init .Cm init
.Nm .Nm
.Cm attach .Cm attach
.Op Fl dpv .Op Fl dprv
.Op Fl k Ar keyfile .Op Fl k Ar keyfile
.Ar prov .Ar prov
.Nm .Nm
@ -76,7 +77,8 @@ utility:
.Nm .Nm
.Cm onetime .Cm onetime
.Op Fl d .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 l Ar keylen
.Op Fl s Ar sectorsize .Op Fl s Ar sectorsize
.Ar prov ... .Ar prov ...
@ -144,6 +146,16 @@ Supports many cryptographic algorithms (currently
and and
.Nm 3DES ) . .Nm 3DES ) .
.It .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 Can create a key from a couple of components (user entered passphrase, random
bits from a file, etc.). bits from a file, etc.).
.It .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. passphrase before the root file system is mounted.
.It .It
The passphrase of the user is strengthened with: The passphrase of the user is strengthened with:
.Rs .Rs
.%A B. Kaliski .%A B. Kaliski
.%T "PKCS #5: Password-Based Cryptography Specification, Version 2.0." .%T "PKCS #5: Password-Based Cryptography Specification, Version 2.0."
.%R RFC .%R RFC
@ -163,7 +175,7 @@ Allows to use two independent keys (e.g.
and and
.Qq "company key" ) . .Qq "company key" ) .
.It .It
It is fast - It is fast -
.Nm .Nm
performs simple sector-to-sector encryption. performs simple sector-to-sector encryption.
.It .It
@ -177,6 +189,8 @@ the file systems).
.It .It
Allows to attach a provider with a random, one-time key - useful for swap Allows to attach a provider with a random, one-time key - useful for swap
partitions and temporary file systems. partitions and temporary file systems.
.It
Allows to verify data integrity (data authentication).
.El .El
.Pp .Pp
The first argument to 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. The last provider's sector is used to store metadata.
.Pp .Pp
Additional options include: Additional options include:
.Bl -tag -width ".Fl a Ar algo" .Bl -tag -width ".Fl a Ar aalgo"
.It Fl a Ar algo .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. Encryption algorithm to use.
Currently supported algorithms are: Currently supported algorithms are:
.Nm AES , .Nm AES ,
@ -259,6 +288,9 @@ Probably a better choice is the
option for the option for the
.Cm detach .Cm detach
subcommand. subcommand.
.It Fl r
Attach read-only provider.
It will not be opened for writing.
.It Fl k Ar keyfile .It Fl k Ar keyfile
Specifies a file which contains part of the key. Specifies a file which contains part of the key.
For more information see the description of the 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. The command can be used to encrypt swap partitions or temporary file systems.
.Pp .Pp
Additional options include: Additional options include:
.Bl -tag -width ".Fl a Ar algo" .Bl -tag -width ".Fl a Ar aalgo"
.It Fl a Ar algo .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. Encryption algorithm to use.
For more information, see the description of the For more information, see the description of the
.Cm init .Cm init
@ -415,6 +452,8 @@ variables can be used to control the behavior of the
.Nm ELI .Nm ELI
GEOM class. GEOM class.
The default value is shown next to each variable. The default value is shown next to each variable.
All variables can also be set in
.Pa /boot/loader.conf .
.Bl -tag -width indent .Bl -tag -width indent
.It Va kern.geom.eli.debug : No 0 .It Va kern.geom.eli.debug : No 0
Debug level of the 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 0, minimal debug information is printed.
If set to 3, the If set to 3, the
maximum amount of debug information is printed. 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 .It Va kern.geom.eli.tries : No 3
Number of times a user is asked for the passphrase. Number of times a user is asked for the passphrase.
This is only used for providers which should be attached on boot 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. Its purpose is to increase performance on SMP systems.
If hardware acceleration is available, only one thread will be started. 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. If set to 0, CPU-bound thread will be started for every active CPU.
This variable could be set in .It Va kern.geom.eli.batch : No 0
.Pa /boot/loader.conf . 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 .El
.Sh EXIT STATUS .Sh EXIT STATUS
Exit status is 0 on success, and 1 if the command fails. Exit status is 0 on success, and 1 if the command fails.
@ -508,7 +548,7 @@ Reenter new passphrase:
Encrypted swap partition setup: Encrypted swap partition setup:
.Bd -literal -offset indent .Bd -literal -offset indent
# dd if=/dev/random of=/dev/ad0s1b bs=1m # 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 # swapon /dev/ad0s1b.eli
.Ed .Ed
.Pp .Pp
@ -546,6 +586,38 @@ geli_da1s3a_keyfile0_load="YES"
geli_da1s3a_keyfile0_type="da1s3a:geli_keyfile0" geli_da1s3a_keyfile0_type="da1s3a:geli_keyfile0"
geli_da1s3a_keyfile0_name="/boot/keys/da1s3a.key" geli_da1s3a_keyfile0_name="/boot/keys/da1s3a.key"
.Ed .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 .Sh SEE ALSO
.Xr crypto 4 , .Xr crypto 4 ,
.Xr gbde 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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 lib_version = G_LIB_VERSION;
uint32_t version = G_ELI_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 keylen = 0;
static intmax_t keyno = -1; static intmax_t keyno = -1;
static intmax_t iterations = -1; static intmax_t iterations = -1;
@ -75,12 +76,12 @@ static void eli_dump(struct gctl_req *req);
/* /*
* Available commands: * 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' * label - alias for 'init'
* attach [-dpv] [-k keyfile] prov * attach [-dprv] [-k keyfile] prov
* detach [-fl] prov ... * detach [-fl] prov ...
* stop - alias for 'detach' * 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 * setkey [-pPv] [-n keyno] [-k keyfile] [-K newkeyfile] prov
* delkey [-afv] [-n keyno] prov * delkey [-afv] [-n keyno] prov
* kill [-av] [prov ...] * kill [-av] [prov ...]
@ -92,8 +93,9 @@ static void eli_dump(struct gctl_req *req);
struct g_command class_commands[] = { struct g_command class_commands[] = {
{ "init", G_FLAG_VERBOSE, eli_main, { "init", G_FLAG_VERBOSE, eli_main,
{ {
{ 'a', "algo", algo, G_TYPE_STRING }, { 'a', "aalgo", aalgo, G_TYPE_STRING },
{ 'b', "boot", NULL, G_TYPE_NONE }, { 'b', "boot", NULL, G_TYPE_NONE },
{ 'e', "ealgo", ealgo, G_TYPE_STRING },
{ 'i', "iterations", &iterations, G_TYPE_NUMBER }, { 'i', "iterations", &iterations, G_TYPE_NUMBER },
{ 'K', "newkeyfile", newkeyfile, G_TYPE_STRING }, { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
{ 'l', "keylen", &keylen, G_TYPE_NUMBER }, { 'l', "keylen", &keylen, G_TYPE_NUMBER },
@ -101,12 +103,13 @@ struct g_command class_commands[] = {
{ 's', "sectorsize", &sectorsize, G_TYPE_NUMBER }, { 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
G_OPT_SENTINEL 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, { "label", G_FLAG_VERBOSE, eli_main,
{ {
{ 'a', "algo", algo, G_TYPE_STRING }, { 'a', "aalgo", aalgo, G_TYPE_STRING },
{ 'b', "boot", NULL, G_TYPE_NONE }, { 'b', "boot", NULL, G_TYPE_NONE },
{ 'e', "ealgo", ealgo, G_TYPE_STRING },
{ 'i', "iterations", &iterations, G_TYPE_NUMBER }, { 'i', "iterations", &iterations, G_TYPE_NUMBER },
{ 'K', "newkeyfile", newkeyfile, G_TYPE_STRING }, { 'K', "newkeyfile", newkeyfile, G_TYPE_STRING },
{ 'l', "keylen", &keylen, G_TYPE_NUMBER }, { 'l', "keylen", &keylen, G_TYPE_NUMBER },
@ -121,9 +124,10 @@ struct g_command class_commands[] = {
{ 'd', "detach", NULL, G_TYPE_NONE }, { 'd', "detach", NULL, G_TYPE_NONE },
{ 'k', "keyfile", keyfile, G_TYPE_STRING }, { 'k', "keyfile", keyfile, G_TYPE_STRING },
{ 'p', "nopassphrase", NULL, G_TYPE_NONE }, { 'p', "nopassphrase", NULL, G_TYPE_NONE },
{ 'r', "readonly", NULL, G_TYPE_NONE },
G_OPT_SENTINEL G_OPT_SENTINEL
}, },
"[-dpv] [-k keyfile] prov" "[-dprv] [-k keyfile] prov"
}, },
{ "detach", 0, NULL, { "detach", 0, NULL,
{ {
@ -143,13 +147,14 @@ struct g_command class_commands[] = {
}, },
{ "onetime", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, { "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 }, { 'd', "detach", NULL, G_TYPE_NONE },
{ 'e', "ealgo", ealgo, G_TYPE_STRING },
{ 'l', "keylen", &keylen, G_TYPE_NUMBER }, { 'l', "keylen", &keylen, G_TYPE_NUMBER },
{ 's', "sectorsize", &sectorsize, G_TYPE_NUMBER }, { 's', "sectorsize", &sectorsize, G_TYPE_NUMBER },
G_OPT_SENTINEL 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, { "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) { if (md->md_iterations == 0) {
g_eli_crypto_hmac_update(&ctx, md->md_salt, g_eli_crypto_hmac_update(&ctx, md->md_salt,
@ -523,16 +528,44 @@ eli_init(struct gctl_req *req)
md.md_flags = 0; md.md_flags = 0;
if (gctl_get_int(req, "boot")) if (gctl_get_int(req, "boot"))
md.md_flags |= G_ELI_FLAG_BOOT; md.md_flags |= G_ELI_FLAG_BOOT;
str = gctl_get_ascii(req, "algo"); md.md_ealgo = CRYPTO_ALGORITHM_MIN - 1;
md.md_algo = g_eli_str2algo(str); str = gctl_get_ascii(req, "aalgo");
if (md.md_algo < CRYPTO_ALGORITHM_MIN || if (strcmp(str, "none") != 0) {
md.md_algo > CRYPTO_ALGORITHM_MAX) { md.md_aalgo = g_eli_str2aalgo(str);
gctl_error(req, "Invalid encryption algorithm."); if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
return; 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"); val = gctl_get_intmax(req, "keylen");
md.md_keylen = val; 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) { if (md.md_keylen == 0) {
gctl_error(req, "Invalid key length."); gctl_error(req, "Invalid key length.");
return; return;
@ -579,7 +612,7 @@ eli_init(struct gctl_req *req)
} }
/* Encrypt the first and the only Master Key. */ /* 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)); bzero(key, sizeof(key));
if (error != 0) { if (error != 0) {
bzero(&md, sizeof(md)); 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. */ /* 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)); bzero(key, sizeof(key));
if (error != 0) { if (error != 0) {
bzero(md, sizeof(*md)); 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * 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, &g_eli_visible_passphrase, 0,
"Turn on echo when entering the passphrase (for debug purposes only!!)"); "Turn on echo when entering the passphrase (for debug purposes only!!)");
u_int g_eli_overwrites = 5; 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, 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"); 0, "Number of times on-disk keys should be overwritten when destroying them");
static u_int g_eli_threads = 0; static u_int g_eli_threads = 0;
TUNABLE_INT("kern.geom.eli.threads", &g_eli_threads); TUNABLE_INT("kern.geom.eli.threads", &g_eli_threads);
SYSCTL_UINT(_kern_geom_eli, OID_AUTO, threads, CTLFLAG_RW, &g_eli_threads, 0, SYSCTL_UINT(_kern_geom_eli, OID_AUTO, threads, CTLFLAG_RW, &g_eli_threads, 0,
"Number of threads doing crypto work"); "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, static int g_eli_destroy_geom(struct gctl_req *req, struct g_class *mp,
struct g_geom *gp); 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_taste_t g_eli_taste;
static g_dumpconf_t g_eli_dumpconf; static g_dumpconf_t g_eli_dumpconf;
@ -106,7 +110,7 @@ struct g_class g_eli_class = {
* accelerator or something like this. * accelerator or something like this.
* The function updates the SID and rerun the operation. * The function updates the SID and rerun the operation.
*/ */
static int int
g_eli_crypto_rerun(struct cryptop *crp) g_eli_crypto_rerun(struct cryptop *crp)
{ {
struct g_eli_softc *sc; 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 * 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) g_eli_read_done(struct bio *bp)
{ {
struct g_eli_softc *sc; struct g_eli_softc *sc;
@ -149,10 +153,20 @@ g_eli_read_done(struct bio *bp)
pbp = bp->bio_parent; pbp = bp->bio_parent;
if (pbp->bio_error == 0) if (pbp->bio_error == 0)
pbp->bio_error = bp->bio_error; 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); g_destroy_bio(bp);
if (pbp->bio_error != 0) { if (pbp->bio_error != 0) {
G_ELI_LOGREQ(0, pbp, "%s() failed", __func__); G_ELI_LOGREQ(0, pbp, "%s() failed", __func__);
pbp->bio_completed = 0; 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); g_io_deliver(pbp, pbp->bio_error);
return; return;
} }
@ -163,70 +177,31 @@ g_eli_read_done(struct bio *bp)
wakeup(sc); 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. * 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 * 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) g_eli_write_done(struct bio *bp)
{ {
struct bio *pbp; struct bio *pbp;
G_ELI_LOGREQ(2, bp, "Request done."); G_ELI_LOGREQ(2, bp, "Request done.");
pbp = bp->bio_parent; pbp = bp->bio_parent;
if (pbp->bio_error == 0) if (pbp->bio_error == 0) {
pbp->bio_error = bp->bio_error; 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); free(pbp->bio_driver2, M_ELI);
pbp->bio_driver2 = NULL; pbp->bio_driver2 = NULL;
if (pbp->bio_error == 0) if (pbp->bio_error != 0) {
pbp->bio_completed = pbp->bio_length;
else {
G_ELI_LOGREQ(0, pbp, "Crypto WRITE request failed (error=%d).", G_ELI_LOGREQ(0, pbp, "Crypto WRITE request failed (error=%d).",
pbp->bio_error); pbp->bio_error);
pbp->bio_completed = 0; pbp->bio_completed = 0;
@ -235,67 +210,10 @@ g_eli_write_done(struct bio *bp)
/* /*
* Write is finished, send it up. * Write is finished, send it up.
*/ */
pbp->bio_completed = pbp->bio_length;
g_io_deliver(pbp, pbp->bio_error); 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() * This function should never be called, but GEOM made as it set ->orphan()
* method for every geom. * method for every geom.
@ -358,15 +276,20 @@ g_eli_start(struct bio *bp)
} }
switch (bp->bio_cmd) { switch (bp->bio_cmd) {
case BIO_READ: case BIO_READ:
cbp->bio_done = g_eli_read_done; if (!(sc->sc_flags & G_ELI_FLAG_AUTH)) {
cp = LIST_FIRST(&sc->sc_geom->consumer); bp->bio_driver2 = NULL;
cbp->bio_to = cp->provider; cbp->bio_done = g_eli_read_done;
G_ELI_LOGREQ(2, cbp, "Sending request."); cp = LIST_FIRST(&sc->sc_geom->consumer);
/* cbp->bio_to = cp->provider;
* Read encrypted data from provider. G_ELI_LOGREQ(2, cbp, "Sending request.");
*/ /*
g_io_request(cbp, cp); * Read encrypted data from provider.
break; */
g_io_request(cbp, cp);
break;
}
bp->bio_pflags = 255;
/* FALLTHROUGH */
case BIO_WRITE: case BIO_WRITE:
bp->bio_driver1 = cbp; bp->bio_driver1 = cbp;
mtx_lock(&sc->sc_queue_mtx); mtx_lock(&sc->sc_queue_mtx);
@ -411,7 +334,7 @@ g_eli_worker(void *arg)
mtx_lock(&sc->sc_queue_mtx); mtx_lock(&sc->sc_queue_mtx);
bp = bioq_takefirst(&sc->sc_queue); bp = bioq_takefirst(&sc->sc_queue);
if (bp == NULL) { 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); LIST_REMOVE(wr, w_next);
crypto_freesession(wr->w_sid); crypto_freesession(wr->w_sid);
free(wr, M_ELI); free(wr, M_ELI);
@ -426,20 +349,27 @@ g_eli_worker(void *arg)
continue; continue;
} }
mtx_unlock(&sc->sc_queue_mtx); 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. * 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, g_eli_crypto_ivgen(struct g_eli_softc *sc, off_t offset, u_char *iv,
size_t size) size_t size)
{ {
u_char hash[SHA256_DIGEST_LENGTH]; u_char off[8], hash[SHA256_DIGEST_LENGTH];
SHA256_CTX ctx; 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. */ /* Copy precalculated SHA256 context for IV-Key. */
bcopy(&sc->sc_ivctx, &ctx, sizeof(ctx)); bcopy(&sc->sc_ivctx, &ctx, sizeof(ctx));
SHA256_Update(&ctx, (uint8_t *)&offset, sizeof(offset)); 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); 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 int
g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, g_eli_read_metadata(struct g_class *mp, struct g_provider *pp,
struct g_eli_metadata *md) 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; sc = gp->softc;
if (dw > 0) { 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. */ /* Someone is opening us for write, we need to remember that. */
sc->sc_flags |= G_ELI_FLAG_WOPEN; sc->sc_flags |= G_ELI_FLAG_WOPEN;
return (0); 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_geom *gp;
struct g_provider *pp; struct g_provider *pp;
struct g_consumer *cp; struct g_consumer *cp;
struct cryptoini cri; struct cryptoini crie, cria;
u_int i, threads; u_int i, threads;
int error; 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; gp->start = g_eli_start;
/* /*
* Spoiling cannot happen actually, because we keep provider open for * 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->spoiled = g_eli_orphan_spoil_assert;
gp->orphan = g_eli_orphan; gp->orphan = g_eli_orphan;
gp->dumpconf = g_eli_dumpconf;
/* /*
* If detach-on-last-close feature is not enabled, we can simply use * If detach-on-last-close feature is not enabled and we don't operate
* g_std_access(). * 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; gp->access = g_eli_access;
else else
gp->access = g_std_access; gp->access = g_std_access;
gp->dumpconf = g_eli_dumpconf;
sc->sc_crypto = G_ELI_CRYPTO_SW; sc->sc_crypto = G_ELI_CRYPTO_SW;
sc->sc_flags = md->md_flags; 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; sc->sc_nkey = nkey;
/* /*
* Remember the keys in our softc structure. * Remember the keys in our softc structure.
*/ */
bcopy(mkey, sc->sc_ivkey, sizeof(sc->sc_ivkey)); g_eli_mkey_propagate(sc, mkey);
mkey += sizeof(sc->sc_ivkey); sc->sc_ekeylen = md->md_keylen;
bcopy(mkey, sc->sc_datakey, sizeof(sc->sc_datakey));
sc->sc_keylen = 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. * 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, * Keep provider open all the time, so we can run critical tasks,
* like Master Keys deletion, without wondering if we can open * like Master Keys deletion, without wondering if we can open
* provider or not. * 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 (error != 0) {
if (req != NULL) { if (req != NULL) {
gctl_error(req, "Cannot access %s (error=%d).", 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); LIST_INIT(&sc->sc_workers);
bzero(&cri, sizeof(cri)); bzero(&crie, sizeof(crie));
cri.cri_alg = sc->sc_algo; crie.cri_alg = sc->sc_ealgo;
cri.cri_klen = sc->sc_keylen; crie.cri_klen = sc->sc_ekeylen;
cri.cri_key = sc->sc_datakey; 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; threads = g_eli_threads;
if (threads == 0) 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. * Use software cryptography, if we cannot get it.
*/ */
if (i == 0) { if (i == 0) {
error = crypto_newsession(&wr->w_sid, &cri, 1); error = crypto_newsession(&wr->w_sid, &crie, 1);
if (error == 0) if (error == 0)
sc->sc_crypto = G_ELI_CRYPTO_HW; sc->sc_crypto = G_ELI_CRYPTO_HW;
} }
if (sc->sc_crypto == G_ELI_CRYPTO_SW) 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) { if (error != 0) {
free(wr, M_ELI); free(wr, M_ELI);
if (req != NULL) { 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 = g_new_providerf(gp, "%s%s", bpp->name, G_ELI_SUFFIX);
pp->sectorsize = md->md_sectorsize; pp->sectorsize = md->md_sectorsize;
pp->mediasize = bpp->mediasize; 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 -= 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_error_provider(pp, 0);
G_ELI_DEBUG(0, "Device %s created.", pp->name); 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, "Encryption: %s %u", g_eli_algo2str(sc->sc_ealgo),
G_ELI_DEBUG(0, "Key length: %u", sc->sc_keylen); 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", G_ELI_DEBUG(0, " Crypto: %s",
sc->sc_crypto == G_ELI_CRYPTO_SW ? "software" : "hardware"); sc->sc_crypto == G_ELI_CRYPTO_SW ? "software" : "hardware");
return (gp); 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) if (md.md_provsize != pp->mediasize)
return (NULL); return (NULL);
/* Should we attach it on boot? */ /* 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); return (NULL);
if (md.md_keys == 0x00) { if (md.md_keys == 0x00) {
G_ELI_DEBUG(0, "No valid keys on %s.", pp->name); 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; int first = 1;
#define ADD_FLAG(flag, name) do { \ #define ADD_FLAG(flag, name) do { \
if ((sc->sc_flags & (flag)) != 0) { \ if (sc->sc_flags & (flag)) { \
if (!first) \ if (!first) \
sbuf_printf(sb, ", "); \ sbuf_printf(sb, ", "); \
else \ else \
@ -1125,17 +1004,20 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
sbuf_printf(sb, name); \ sbuf_printf(sb, name); \
} \ } \
} while (0) } 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_ONETIME, "ONETIME");
ADD_FLAG(G_ELI_FLAG_BOOT, "BOOT"); ADD_FLAG(G_ELI_FLAG_BOOT, "BOOT");
ADD_FLAG(G_ELI_FLAG_WO_DETACH, "W-DETACH"); ADD_FLAG(G_ELI_FLAG_WO_DETACH, "W-DETACH");
ADD_FLAG(G_ELI_FLAG_RW_DETACH, "RW-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_WOPEN, "W-OPEN");
ADD_FLAG(G_ELI_FLAG_DESTROY, "DESTROY"); ADD_FLAG(G_ELI_FLAG_DESTROY, "DESTROY");
ADD_FLAG(G_ELI_FLAG_RO, "READ-ONLY");
#undef ADD_FLAG #undef ADD_FLAG
} }
sbuf_printf(sb, "</Flags>\n"); 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, sbuf_printf(sb, "%s<UsedKey>%u</UsedKey>\n", indent,
sc->sc_nkey); sc->sc_nkey);
} }
@ -1152,9 +1034,15 @@ g_eli_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
break; break;
} }
sbuf_printf(sb, "</Crypto>\n"); sbuf_printf(sb, "</Crypto>\n");
sbuf_printf(sb, "%s<KeyLength>%u</KeyLength>\n", indent, sc->sc_keylen); if (sc->sc_flags & G_ELI_FLAG_AUTH) {
sbuf_printf(sb, "%s<Cipher>%s</Cipher>\n", indent, sbuf_printf(sb,
g_eli_algo2str(sc->sc_algo)); "%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); 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -54,28 +54,43 @@
/* /*
* Version history: * Version history:
* 0 - Initial version number. * 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. */ /* 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. */ /* 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. */ /* 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. */ /* 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. */ /* Provider was open for writing. */
#define G_ELI_FLAG_WOPEN 0x00010000 #define G_ELI_FLAG_WOPEN 0x00010000
/* Destroy device. */ /* 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 SHA512_MDLEN 64
#define G_ELI_AUTH_SECKEYLEN SHA256_DIGEST_LENGTH
#define G_ELI_MAXMKEYS 2 #define G_ELI_MAXMKEYS 2
#define G_ELI_MAXKEYLEN 64 #define G_ELI_MAXKEYLEN 64
#define G_ELI_USERKEYLEN G_ELI_MAXKEYLEN #define G_ELI_USERKEYLEN G_ELI_MAXKEYLEN
#define G_ELI_DATAKEYLEN 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_IVKEYLEN G_ELI_MAXKEYLEN
#define G_ELI_SALTLEN 64 #define G_ELI_SALTLEN 64
#define G_ELI_DATAIVKEYLEN (G_ELI_DATAKEYLEN + G_ELI_IVKEYLEN) #define G_ELI_DATAIVKEYLEN (G_ELI_DATAKEYLEN + G_ELI_IVKEYLEN)
@ -85,6 +100,7 @@
#ifdef _KERNEL #ifdef _KERNEL
extern u_int g_eli_debug; extern u_int g_eli_debug;
extern u_int g_eli_overwrites; extern u_int g_eli_overwrites;
extern u_int g_eli_batch;
#define G_ELI_CRYPTO_HW 1 #define G_ELI_CRYPTO_HW 1
#define G_ELI_CRYPTO_SW 2 #define G_ELI_CRYPTO_SW 2
@ -123,13 +139,21 @@ struct g_eli_worker {
struct g_eli_softc { struct g_eli_softc {
struct g_geom *sc_geom; struct g_geom *sc_geom;
u_int sc_crypto; 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]; uint8_t sc_ivkey[G_ELI_IVKEYLEN];
SHA256_CTX sc_ivctx; SHA256_CTX sc_ivctx;
u_int sc_algo;
u_int sc_keylen;
int sc_nkey; int sc_nkey;
uint32_t sc_flags; uint32_t sc_flags;
u_int sc_bytes_per_sector;
u_int sc_data_per_sector;
/* Only for software cryptography. */ /* Only for software cryptography. */
struct bio_queue_head sc_queue; struct bio_queue_head sc_queue;
@ -143,8 +167,9 @@ struct g_eli_metadata {
char md_magic[16]; /* Magic value. */ char md_magic[16]; /* Magic value. */
uint32_t md_version; /* Version number. */ uint32_t md_version; /* Version number. */
uint32_t md_flags; /* Additional flags. */ 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_keylen; /* Key length. */
uint16_t md_aalgo; /* Authentication algorithm. */
uint64_t md_provsize; /* Provider's size. */ uint64_t md_provsize; /* Provider's size. */
uint32_t md_sectorsize; /* Sector size. */ uint32_t md_sectorsize; /* Sector size. */
uint8_t md_keys; /* Available keys. */ 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); 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_version); p += sizeof(md->md_version);
le32enc(p, md->md_flags); p += sizeof(md->md_flags); 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_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); le64enc(p, md->md_provsize); p += sizeof(md->md_provsize);
le32enc(p, md->md_sectorsize); p += sizeof(md->md_sectorsize); le32enc(p, md->md_sectorsize); p += sizeof(md->md_sectorsize);
*p = md->md_keys; p += sizeof(md->md_keys); *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); p = data + sizeof(md->md_magic) + sizeof(md->md_version);
md->md_flags = le32dec(p); p += sizeof(md->md_flags); 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_keylen = le16dec(p); p += sizeof(md->md_keylen);
md->md_provsize = le64dec(p); p += sizeof(md->md_provsize); md->md_provsize = le64dec(p); p += sizeof(md->md_provsize);
md->md_sectorsize = le32dec(p); p += sizeof(md->md_sectorsize); 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); return (0);
} }
static __inline int 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) eli_metadata_decode(const u_char *data, struct g_eli_metadata *md)
{ {
int error; int error;
@ -212,6 +262,10 @@ eli_metadata_decode(const u_char *data, struct g_eli_metadata *md)
case 0: case 0:
error = eli_metadata_decode_v0(data, md); error = eli_metadata_decode_v0(data, md);
break; break;
case 1:
case 2:
error = eli_metadata_decode_v1v2(data, md);
break;
default: default:
error = EINVAL; error = EINVAL;
break; break;
@ -221,7 +275,7 @@ eli_metadata_decode(const u_char *data, struct g_eli_metadata *md)
#endif /* !_OpenSSL */ #endif /* !_OpenSSL */
static __inline u_int static __inline u_int
g_eli_str2algo(const char *name) g_eli_str2ealgo(const char *name)
{ {
if (strcasecmp("null", name) == 0) if (strcasecmp("null", name) == 0)
@ -235,6 +289,25 @@ g_eli_str2algo(const char *name)
return (CRYPTO_ALGORITHM_MIN - 1); 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 * static __inline const char *
g_eli_algo2str(u_int algo) g_eli_algo2str(u_int algo)
{ {
@ -243,11 +316,23 @@ g_eli_algo2str(u_int algo)
case CRYPTO_NULL_CBC: case CRYPTO_NULL_CBC:
return ("NULL"); return ("NULL");
case CRYPTO_AES_CBC: case CRYPTO_AES_CBC:
return ("AES"); return ("AES-CBC");
case CRYPTO_BLF_CBC: case CRYPTO_BLF_CBC:
return ("Blowfish"); return ("Blowfish-CBC");
case CRYPTO_3DES_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"); return ("unknown");
} }
@ -262,8 +347,10 @@ eli_metadata_dump(const struct g_eli_metadata *md)
printf(" magic: %s\n", md->md_magic); printf(" magic: %s\n", md->md_magic);
printf(" version: %u\n", (u_int)md->md_version); printf(" version: %u\n", (u_int)md->md_version);
printf(" flags: 0x%x\n", (u_int)md->md_flags); 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); 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(" provsize: %ju\n", (uintmax_t)md->md_provsize);
printf("sectorsize: %u\n", (u_int)md->md_sectorsize); printf("sectorsize: %u\n", (u_int)md->md_sectorsize);
printf(" keys: 0x%02x\n", (u_int)md->md_keys); 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 #ifdef _KERNEL
int g_eli_read_metadata(struct g_class *mp, struct g_provider *pp, int g_eli_read_metadata(struct g_class *mp, struct g_provider *pp,
struct g_eli_metadata *md); 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); 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_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 #endif
void g_eli_mkey_hmac(unsigned char *mkey, const unsigned char *key); 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); const unsigned char *key, unsigned char *mkey, unsigned *nkeyp);
int g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen, int g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen,
unsigned char *mkey); 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, int g_eli_crypto_encrypt(u_int algo, u_char *data, size_t datasize,
const u_char *key, size_t keysize); 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; struct g_provider *pp;
const char *name; const char *name;
u_char *key, mkey[G_ELI_DATAIVKEYLEN]; u_char *key, mkey[G_ELI_DATAIVKEYLEN];
int *nargs, *detach; int *nargs, *detach, *readonly;
int keysize, error; int keysize, error;
u_int nkey; u_int nkey;
@ -79,6 +79,12 @@ g_eli_ctl_attach(struct gctl_req *req, struct g_class *mp)
return; 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"); name = gctl_get_asciiparam(req, "arg0");
if (name == NULL) { if (name == NULL) {
gctl_error(req, "No 'arg%u' argument.", 0); 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); 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) if (*detach)
md.md_flags |= G_ELI_FLAG_WO_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); g_eli_create(req, mp, pp, &md, mkey, nkey);
bzero(mkey, sizeof(mkey)); bzero(mkey, sizeof(mkey));
bzero(&md, sizeof(md)); bzero(&md, sizeof(md));
@ -250,16 +264,49 @@ g_eli_ctl_onetime(struct gctl_req *req, struct g_class *mp)
if (*detach) if (*detach)
md.md_flags |= G_ELI_FLAG_WO_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) { if (name == NULL) {
gctl_error(req, "No '%s' argument.", "algo"); gctl_error(req, "No '%s' argument.", "aalgo");
return; return;
} }
md.md_algo = g_eli_str2algo(name); if (strcmp(name, "none") != 0) {
if (md.md_algo < CRYPTO_ALGORITHM_MIN || md.md_aalgo = g_eli_str2aalgo(name);
md.md_algo > CRYPTO_ALGORITHM_MAX) { if (md.md_aalgo >= CRYPTO_ALGORITHM_MIN &&
gctl_error(req, "Invalid '%s' argument.", "algo"); md.md_aalgo <= CRYPTO_ALGORITHM_MAX) {
return; 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)); 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"); gctl_error(req, "No '%s' argument.", "keylen");
return; 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) { if (md.md_keylen == 0) {
gctl_error(req, "Invalid '%s' argument.", "keylen"); gctl_error(req, "Invalid '%s' argument.", "keylen");
return; 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); gctl_error(req, "Provider %s is invalid.", name);
return; 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); cp = LIST_FIRST(&sc->sc_geom->consumer);
pp = cp->provider; 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; mkeydst = md.md_mkeys + nkey * G_ELI_MKEYLEN;
md.md_keys |= (1 << nkey); md.md_keys |= (1 << nkey);
bcopy(sc->sc_ivkey, mkeydst, sizeof(sc->sc_ivkey)); bcopy(sc->sc_mkey, mkeydst, sizeof(sc->sc_mkey));
bcopy(sc->sc_datakey, mkeydst + sizeof(sc->sc_ivkey),
sizeof(sc->sc_datakey));
/* Encrypt Master Key with the new key. */ /* 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)); bzero(key, sizeof(key));
if (error != 0) { if (error != 0) {
bzero(&md, sizeof(md)); 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); gctl_error(req, "Provider %s is invalid.", name);
return; 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); cp = LIST_FIRST(&sc->sc_geom->consumer);
pp = cp->provider; pp = cp->provider;
@ -534,9 +587,7 @@ g_eli_kill_one(struct g_eli_softc *sc)
{ {
struct g_provider *pp; struct g_provider *pp;
struct g_consumer *cp; struct g_consumer *cp;
u_char *sector; int error = 0;
int err, error = 0;
u_int i;
g_topology_assert(); g_topology_assert();
@ -549,22 +600,31 @@ g_eli_kill_one(struct g_eli_softc *sc)
cp = LIST_FIRST(&sc->sc_geom->consumer); cp = LIST_FIRST(&sc->sc_geom->consumer);
pp = cp->provider; pp = cp->provider;
sector = malloc(pp->sectorsize, M_ELI, M_WAITOK); if (sc->sc_flags & G_ELI_FLAG_RO) {
for (i = 0; i <= g_eli_overwrites; i++) { G_ELI_DEBUG(0, "WARNING: Metadata won't be erased on read-only "
if (i == g_eli_overwrites) "provider: %s.", pp->name);
bzero(sector, pp->sectorsize); } else {
else u_char *sector;
arc4rand(sector, pp->sectorsize, 0); u_int i;
err = g_write_data(cp, pp->mediasize - pp->sectorsize, sector, int err;
pp->sectorsize);
if (err != 0) { sector = malloc(pp->sectorsize, M_ELI, M_WAITOK);
G_ELI_DEBUG(0, "Cannot erase metadata on %s " for (i = 0; i <= g_eli_overwrites; i++) {
"(error=%d).", pp->name, err); if (i == g_eli_overwrites)
if (error == 0) bzero(sector, pp->sectorsize);
error = err; 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) if (error == 0)
G_ELI_DEBUG(0, "%s has been killed.", pp->name); G_ELI_DEBUG(0, "%s has been killed.", pp->name);
g_eli_destroy(sc, 1); 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; nkey = 0;
for (nkey = 0; nkey < G_ELI_MAXMKEYS; nkey++, mmkey += G_ELI_MKEYLEN) { for (nkey = 0; nkey < G_ELI_MAXMKEYS; nkey++, mmkey += G_ELI_MKEYLEN) {
bit = (1 << nkey); bit = (1 << nkey);
if ((md->md_keys & bit) == 0) if (!(md->md_keys & bit))
continue; continue;
bcopy(mmkey, tmpmkey, G_ELI_MKEYLEN); 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); G_ELI_MKEYLEN, enckey, md->md_keylen);
if (error != 0) { if (error != 0) {
bzero(tmpmkey, sizeof(tmpmkey)); bzero(tmpmkey, sizeof(tmpmkey));
@ -177,3 +177,33 @@ g_eli_mkey_encrypt(unsigned algo, const unsigned char *key, unsigned keylen,
return (error); 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 .PATH: ${.CURDIR}/../../../geom/eli
KMOD= 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 WARNS?= 2
.include <bsd.kmod.mk> .include <bsd.kmod.mk>

View File

@ -4,11 +4,9 @@
base=`basename $0` base=`basename $0`
no=45 no=45
sectors=100 sectors=100
rnd=`mktemp /tmp/$base.XXXXXX` || exit 1
keyfile=`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 i=1
for cipher in aes:0 aes:128 aes:192 aes:256 \ 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:0 blowfish:128 blowfish:160 blowfish:192 blowfish:224 \
blowfish:256 blowfish:288 blowfish:320 blowfish:352 blowfish:384 \ blowfish:256 blowfish:288 blowfish:320 blowfish:352 blowfish:384 \
blowfish:416 blowfish:448; do blowfish:416 blowfish:448; do
algo=${cipher%%:*} ealgo=${cipher%%:*}
keylen=${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 init -e $ealgo -l $keylen -P -K $keyfile -s $secsize md${no}
geli attach -p -k $keyfile 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` dd if=/dev/random of=${rnd} bs=${secsize} count=${secs} >/dev/null 2>&1
md_ddev=`dd if=/dev/md${no}.eli bs=512 count=${sectors} 2>/dev/null | md5` dd if=${rnd} of=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null
md_edev=`dd if=/dev/md${no} bs=512 count=${sectors} 2>/dev/null | md5`
if [ ${md_rnd} = ${md_ddev} ]; then md_rnd=`dd if=${rnd} bs=${secsize} count=${secs} 2>/dev/null | md5`
echo "ok $i - ${cipher}" md_ddev=`dd if=/dev/md${no}.eli bs=${secsize} count=${secs} 2>/dev/null | md5`
else md_edev=`dd if=/dev/md${no} bs=${secsize} count=${secs} 2>/dev/null | md5`
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))
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 done
mdconfig -d -u $no rm -f $keyfile
rm -f $rnd $keyfile

View File

@ -4,10 +4,8 @@
base=`basename $0` base=`basename $0`
no=45 no=45
sectors=100 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 i=1
for cipher in aes:0 aes:128 aes:192 aes:256 \ 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:0 blowfish:128 blowfish:160 blowfish:192 blowfish:224 \
blowfish:256 blowfish:288 blowfish:320 blowfish:352 blowfish:384 \ blowfish:256 blowfish:288 blowfish:320 blowfish:352 blowfish:384 \
blowfish:416 blowfish:448; do blowfish:416 blowfish:448; do
algo=${cipher%%:*} ealgo=${cipher%%:*}
keylen=${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_rnd=`dd if=${rnd} bs=${secsize} count=${secs} 2>/dev/null | md5`
md_ddev=`dd if=/dev/md${no}.eli bs=512 count=${sectors} 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=512 count=${sectors} 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 if [ ${md_rnd} = ${md_ddev} ]; then
echo "ok $i - ${cipher}" echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
else else
echo "not ok $i - ${cipher}" echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
fi fi
i=$((i+1)) i=$((i+1))
if [ ${md_rnd} != ${md_edev} ]; then if [ ${md_rnd} != ${md_edev} ]; then
echo "ok $i - ${cipher}" echo "ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
else else
echo "not ok $i - ${cipher}" echo "not ok $i - ealgo=${ealgo} keylen=${keylen} sec=${secsize}"
fi fi
i=$((i+1)) i=$((i+1))
geli detach md${no} geli detach md${no}
rm -f $rnd
mdconfig -d -u $no
done
done done
mdconfig -d -u $no
rm -f $rnd