From fb5d52421913c681ec57796c1f598869199fd5c4 Mon Sep 17 00:00:00 2001 From: Taylor R Campbell Date: Sat, 6 Aug 2022 15:54:22 +0000 Subject: [PATCH 5/5] cgdconfig(8): Add support for generating shared-key parameters files. Usage model: - Generate a parameters file that supports sharing its main key: cgdconfig -g -S -o /etc/cgd/wd0e -V gpt adiantum - Make another parameters file that uses the same shared main key but derives an independent subkey from it: cgdconfig -g -S -P /etc/cgd/wd0e -o /etc/cgd/ld1e \ -V disklabel aes-cbc 256 --- sbin/cgdconfig/cgdconfig.8 | 32 +++++++++- sbin/cgdconfig/cgdconfig.c | 78 +++++++++++++++++++++---- sbin/cgdconfig/params.c | 117 +++++++++++++++++++++++++++++++++++++ sbin/cgdconfig/params.h | 3 + 4 files changed, 217 insertions(+), 13 deletions(-) diff --git a/sbin/cgdconfig/cgdconfig.8 b/sbin/cgdconfig/cgdconfig.8 index f91a2438f1ec..253435157601 100644 --- a/sbin/cgdconfig/cgdconfig.8 +++ b/sbin/cgdconfig/cgdconfig.8 @@ -52,11 +52,12 @@ .Ar paramsfile .Nm .Fl g -.Op Fl v +.Op Fl Sv .Op Fl V Ar vmeth .Op Fl i Ar ivmeth .Op Fl k Ar kgmeth .Op Fl o Ar outfile +.Op Fl P Ar paramsfile .Ar alg .Op Ar keylen .Nm @@ -120,6 +121,22 @@ from before released in 2010; see .Xr cgd 4 for details. +.It Fl P Ar paramsfile +With the +.Fl S +option for the +.Fl g +action, specify a parameters file with a shared key to reuse for +deriving this one as a subkey. +.It Fl S +When generating a parameters file with +.Fl g , +arrange to use a subkey of a shared key. +If +.Fl P Ar paramsfile +is also specified, reuse the shared key of +.Ar paramsfile ; +otherwise a new one will be generated. .It Fl T Generate all keys for all the devices listed in the .Nm @@ -485,6 +502,19 @@ parameters file: new file's passphrase: .Ed .Pp +To create parameters files for three disks with subkeys derived from a +shared password-based key: +.Bd -literal + # cgdconfig -g -S -k argon2id -o /etc/cgd/wd0 -V gpt adiantum + # cgdconfig -g -S -P /etc/cgd/wd0 -o /etc/cgd/ld1 \e + -V disklabel aes-cbc 256 +.Ed +.Pp +Listing these in the same +.Pa /etc/cgd/cgd.conf +will allow you to enter a password once to decrypt both disks with +.Cm cgdconfig -C . +.Pp To configure a cgd that uses aes-cbc with a 192 bit key that it reads from stdin: .Bd -literal diff --git a/sbin/cgdconfig/cgdconfig.c b/sbin/cgdconfig/cgdconfig.c index a146528c3658..b609562ff3cc 100644 --- a/sbin/cgdconfig/cgdconfig.c +++ b/sbin/cgdconfig/cgdconfig.c @@ -100,6 +100,10 @@ enum action { int nflag = 0; +/* if Sflag is set, generate shared keys */ + +int Sflag = 0; + /* if pflag is set to PFLAG_STDIN read from stdin rather than getpass(3) */ #define PFLAG_GETPASS 0x01 @@ -123,7 +127,8 @@ LIST_HEAD(, sharedkey) sharedkeys; static int configure(int, char **, struct params *, int); static int configure_stdin(struct params *, int argc, char **); -static int generate(struct params *, int, char **, const char *); +static int generate(struct params *, int, char **, const char *, + const char *); static int generate_convert(struct params *, int, char **, const char *); static int unconfigure(int, char **, struct params *, int); static int do_all(const char *, int, char **, @@ -176,8 +181,8 @@ usage(void) getprogname()); (void)fprintf(stderr, " %s -G [-enpv] [-i ivmeth] [-k kgmeth] " "[-o outfile] paramsfile\n", getprogname()); - (void)fprintf(stderr, " %s -g [-v] [-i ivmeth] [-k kgmeth] " - "[-o outfile] alg [keylen]\n", getprogname()); + (void)fprintf(stderr, " %s -g [-Sv] [-i ivmeth] [-k kgmeth] " + "[-P paramsfile] [-o outfile] alg [keylen]\n", getprogname()); (void)fprintf(stderr, " %s -l [-v[v]] [cgd]\n", getprogname()); (void)fprintf(stderr, " %s -s [-nv] [-i ivmeth] cgd dev alg " "[keylen]\n", getprogname()); @@ -229,6 +234,7 @@ main(int argc, char **argv) int ch; const char *cfile = NULL; const char *outfile = NULL; + const char *Pfile = NULL; setprogname(*argv); if (hkdf_hmac_sha256_selftest()) @@ -239,7 +245,7 @@ main(int argc, char **argv) p = params_new(); kg = NULL; - while ((ch = getopt(argc, argv, "CGTUV:b:ef:gi:k:lno:sptuv")) != -1) + while ((ch = getopt(argc, argv, "CGP:STUV:b:ef:gi:k:lno:sptuv")) != -1) switch (ch) { case 'C': set_action(&action, ACTION_CONFIGALL); @@ -247,6 +253,14 @@ main(int argc, char **argv) case 'G': set_action(&action, ACTION_GENERATE_CONVERT); break; + case 'P': + if (Pfile) + usage(); + Pfile = estrdup(optarg); + break; + case 'S': + Sflag = 1; + break; case 'T': set_action(&action, ACTION_PRINTALLKEYS); break; @@ -335,6 +349,17 @@ main(int argc, char **argv) err(1, "init failed"); /* validate the consistency of the arguments */ + if (Pfile != NULL && action != ACTION_GENERATE) { + warnx("-P is only for use with -g action"); + usage(); + } + if (Pfile != NULL && !Sflag) { + warnx("-P only makes sense with -S flag"); + } + if (Sflag && action != ACTION_GENERATE) { + warnx("-S is only for use with -g action"); + usage(); + } switch (action) { case ACTION_DEFAULT: /* ACTION_CONFIGURE is the default */ @@ -343,7 +368,7 @@ main(int argc, char **argv) case ACTION_UNCONFIGURE: return unconfigure(argc, argv, NULL, CONFIG_FLAGS_FROMMAIN); case ACTION_GENERATE: - return generate(p, argc, argv, outfile); + return generate(p, argc, argv, outfile, Pfile); case ACTION_GENERATE_CONVERT: return generate_convert(p, argc, argv, outfile); case ACTION_CONFIGALL: @@ -1196,7 +1221,8 @@ verify_reenter(struct params *p) } static int -generate(struct params *p, int argc, char **argv, const char *outfile) +generate(struct params *p, int argc, char **argv, const char *outfile, + const char *Pfile) { int ret; @@ -1218,15 +1244,43 @@ generate(struct params *p, int argc, char **argv, const char *outfile) if (ret) return ret; - if (!p->keygen) { - p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1); - if (!p->keygen) + if (Pfile) { + struct params *pp; + + pp = params_cget(Pfile); + if (pp == NULL) + return -1; + if (!params_verify(pp)) { + params_free(pp); + warnx("invalid parameters file \"%s\"", Pfile); + return -1; + } + p = params_combine(pp, p); + keygen_stripstored(&p->keygen); + if (!p->keygen) { + warnx("no keygen in parameters file \"%s\"", Pfile); return -1; + } + } else { + if (!p->keygen) { + p->keygen = keygen_generate(KEYGEN_PKCS5_PBKDF2_SHA1); + if (!p->keygen) + return -1; + } + + if (keygen_filldefaults(p->keygen, p->keylen)) { + warnx("Failed to generate defaults for keygen"); + return -1; + } } - if (keygen_filldefaults(p->keygen, p->keylen)) { - warnx("Failed to generate defaults for keygen"); - return -1; + if (Sflag) { + if (Pfile) + ret = keygen_tweakshared(p->keygen); + else + ret = keygen_makeshared(p->keygen); + if (ret) + return ret; } if (!params_verify(p)) { diff --git a/sbin/cgdconfig/params.c b/sbin/cgdconfig/params.c index 6154bafa5cb4..ff0c6d34fde5 100644 --- a/sbin/cgdconfig/params.c +++ b/sbin/cgdconfig/params.c @@ -46,6 +46,7 @@ __RCSID("$NetBSD: params.c,v 1.32 2021/11/22 14:34:35 nia Exp $"); #include #include #include +#include #ifdef HAVE_ARGON2 #include @@ -524,6 +525,122 @@ keygen_filldefaults(struct keygen *kg, size_t keylen) return keygen_filldefaults(kg->next, keylen); } +/* + * Strip the storedkey entries in preparation for inserting a shared + * clause with a newly generated info string to derive this key from + * KDF. The result is that the key generated here is independent of + * whatever storedkeys were involved in the old one, so there is no + * need to keep them around, + */ +void +keygen_stripstored(struct keygen **kgp) +{ + struct keygen *kg, *to_free = NULL; + + while ((kg = *kgp) != NULL) { + if (kg->kg_method == KEYGEN_STOREDKEY) { + *kgp = kg->next; + kg->next = to_free; + to_free = kg; + } else { + kgp = &kg->next; + } + } + keygen_free(to_free); +} + +int +keygen_makeshared(struct keygen *kg0) +{ + struct keygen *kg; + + for (kg = kg0; kg != NULL; kg = kg->next) { + switch (kg->kg_method) { + case KEYGEN_RANDOMKEY: + case KEYGEN_URANDOMKEY: + warnx("(u)randomkey keygen cannot be shared"); + return -1; + case KEYGEN_SHELL_CMD: +#ifdef HAVE_ARGON2 + case KEYGEN_ARGON2ID: +#endif + case KEYGEN_PKCS5_PBKDF2_OLD: + case KEYGEN_PKCS5_PBKDF2_SHA1: + break; + case KEYGEN_STOREDKEY: + warnx("storedkey does not make sense as shared"); + return -1; + default: + return -1; + } + if (kg->kg_sharedid != NULL) { + warnx("keygen already shared"); + return -1; + } + } + for (kg = kg0; kg != NULL; kg = kg->next) { + struct uuid id; + char *idstr; + uint32_t status; + + if (uuidgen(&id, 1) == -1) { + warn("uuidgen"); + return -1; + } + uuid_to_string(&id, &idstr, &status); + if (status != uuid_s_ok) { + warnx("uuid_to_string: %"PRIu32, status); + return -1; + } + + kg->kg_sharedid = string_fromcharstar(idstr); + kg->kg_sharedalg = SHARED_ALG_HKDF_HMAC_SHA256; + kg->kg_sharedlen = 8*SHA256_DIGEST_LENGTH; + kg->kg_sharedinfo = bits_getrandombits(DEFAULT_SALTLEN, 0); + + free(idstr); + } + return 0; +} + +int +keygen_tweakshared(struct keygen *kg0) +{ + struct keygen *kg; + + for (kg = kg0; kg != NULL; kg = kg->next) { + switch (kg->kg_method) { + case KEYGEN_RANDOMKEY: + case KEYGEN_URANDOMKEY: + warnx("(u)randomkey keygen cannot be shared"); + return -1; + case KEYGEN_SHELL_CMD: +#ifdef HAVE_ARGON2 + case KEYGEN_ARGON2ID: +#endif + case KEYGEN_PKCS5_PBKDF2_OLD: + case KEYGEN_PKCS5_PBKDF2_SHA1: + break; + case KEYGEN_STOREDKEY: + warnx("storedkey does not make sense as shared"); + return -1; + default: + return -1; + } + if (kg->kg_sharedid == NULL) { + warnx("keygen not shared"); + return -1; + } + } + for (kg = kg0; kg != NULL; kg = kg->next) { + if (kg->kg_method == KEYGEN_STOREDKEY) + continue; + bits_free(kg->kg_sharedinfo); + kg->kg_sharedinfo = bits_getrandombits(DEFAULT_SALTLEN, 0); + } + return 0; +} + struct keygen * keygen_combine(struct keygen *kg1, struct keygen *kg2) { diff --git a/sbin/cgdconfig/params.h b/sbin/cgdconfig/params.h index f2f6fd2307d2..74e32cfc5eb9 100644 --- a/sbin/cgdconfig/params.h +++ b/sbin/cgdconfig/params.h @@ -112,6 +112,9 @@ struct keygen *keygen_new(void); void keygen_free(struct keygen *); int keygen_filldefaults(struct keygen *, size_t); +void keygen_stripstored(struct keygen **); +int keygen_makeshared(struct keygen *); +int keygen_tweakshared(struct keygen *); int keygen_verify(const struct keygen *); void keygen_addlist(struct keygen **, struct keygen *);