Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/tests/dev/cgd cgdconfig(8): Add support for shared keys.



details:   https://anonhg.NetBSD.org/src/rev/8b4e7542c555
branches:  trunk
changeset: 368891:8b4e7542c555
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Fri Aug 12 10:49:17 2022 +0000

description:
cgdconfig(8): Add support for shared keys.

New clause `shared <id> algorithm <alg> subkey <info>' in a keygen
block enables `cgdconfig -C' to reuse a key between different params
files, so you can, e.g., use a single password for multiple disks.
This is better than simply caching the password itself because:

- Hashing the password is expensive, so it should only be done once.

  Suppose your budget is time t before you get bored, and you
  calibrate password hash parameters to unlock n disks before you get
  bored waiting for `cgdconfig -C'.

  . With n password hashings the adversary's cost goes up only by a
    factor of t/n.
  . With one password hashing and n subkeys the adversary's cost goes
    up by a factor of n.

  And if you ever add a disk, rehashing it will make `cgdconfig -C'
  go over budget, whereas another subkey adds negligible cost to you.

- Subkeys work for other types of keygen blocks, like shell_cmd,
  which could be used to get a key from a hardware token that needs a
  button press.

The <info> parameter must be different for each params file;
everything else in the keygen block must be the same.  With this
clause, the keygen block determines a shared key used only to derive
keys; the actual key used by cgdconfig is derived from the shared key
by the specified algorithm.

The only supported algorithm is hkdf-hmac-sha256, which uses
HKDF-Expand of RFC 5869 instantiated with SHA-256.

Example:

        algorithm aes-cbc;
        iv-method encblkno1;
        keylength 128;
        verify_method none;
        keygen pkcs5_pbkdf2/sha1 {
                iterations 39361;
                salt AAAAgMoHiYonye6KogdYJAobCHE=;
                shared "pw" algorithm hkdf-hmac-sha256
                    subkey AAAAgFlw0BMQ5gY+haYkZ6JC+yY=;
        };

The key used for this disk will be derived by

        HKDF-HMAC-SHA256_k(WXDQExDmBj6FpiRnokL7Jg==),

where k is the outcome of PBKDF2-SHA1 with the given parameters.

Note that <info> encodes a four-byte prefix giving the big-endian
length in bits of the info argument to HKDF, just like all other bit
strings in cgdconfig parameters files.

If you have multiple disks configured using the same keygen block
except for the info parameter, `cgdconfig -C' will only prompt once
for your passphrase, generate a shared key k with PBKDF2 as usual,
and then reuse it for each of the disks.

diffstat:

 distrib/sets/lists/tests/mi       |    3 +-
 sbin/cgdconfig/Makefile           |    3 +-
 sbin/cgdconfig/cgdconfig.8        |   93 ++++++++++++-
 sbin/cgdconfig/cgdconfig.c        |  108 ++++++++++++++-
 sbin/cgdconfig/cgdlex.l           |    6 +-
 sbin/cgdconfig/cgdparse.y         |    9 +-
 sbin/cgdconfig/hkdf_hmac_sha256.c |  273 ++++++++++++++++++++++++++++++++++++++
 sbin/cgdconfig/hkdf_hmac_sha256.h |   40 +++++
 sbin/cgdconfig/params.c           |   67 +++++++++-
 sbin/cgdconfig/params.h           |   12 +-
 tests/dev/cgd/Makefile            |    5 +-
 tests/dev/cgd/h_countkey.sh       |   11 +
 tests/dev/cgd/t_cgdconfig.sh      |  154 +++++++++++++++++++++-
 13 files changed, 766 insertions(+), 18 deletions(-)

diffs (truncated from 1122 to 300 lines):

diff -r 8b736c4d92ce -r 8b4e7542c555 distrib/sets/lists/tests/mi
--- a/distrib/sets/lists/tests/mi       Fri Aug 12 10:48:44 2022 +0000
+++ b/distrib/sets/lists/tests/mi       Fri Aug 12 10:49:17 2022 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1218 2022/08/12 10:48:27 riastradh Exp $
+# $NetBSD: mi,v 1.1219 2022/08/12 10:49:17 riastradh Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -1417,6 +1417,7 @@
 ./usr/tests/dev/cgd                                    tests-fs-tests          compattestfile,atf
 ./usr/tests/dev/cgd/Atffile                            tests-fs-tests          compattestfile,atf
 ./usr/tests/dev/cgd/Kyuafile                           tests-fs-tests          compattestfile,atf,kyua
+./usr/tests/dev/cgd/h_countkey                         tests-fs-tests          compattestfile,atf
 ./usr/tests/dev/cgd/h_img2cgd                          tests-obsolete          obsolete
 ./usr/tests/dev/cgd/h_img2cgd/cgd.conf                 tests-obsolete          obsolete
 ./usr/tests/dev/cgd/h_img2cgd/h_img2cgd                        tests-obsolete          obsolete
diff -r 8b736c4d92ce -r 8b4e7542c555 sbin/cgdconfig/Makefile
--- a/sbin/cgdconfig/Makefile   Fri Aug 12 10:48:44 2022 +0000
+++ b/sbin/cgdconfig/Makefile   Fri Aug 12 10:49:17 2022 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.21 2022/05/17 18:56:29 christos Exp $
+# $NetBSD: Makefile,v 1.22 2022/08/12 10:49:17 riastradh Exp $
 
 RUMPPRG=cgdconfig
 MAN=   cgdconfig.8
@@ -8,6 +8,7 @@
 SRCS+= cgdconfig.c             \
        cgdlex.l                \
        cgdparse.y              \
+       hkdf_hmac_sha256.c      \
        pkcs5_pbkdf2.c          \
        params.c                \
        utils.c
diff -r 8b736c4d92ce -r 8b4e7542c555 sbin/cgdconfig/cgdconfig.8
--- a/sbin/cgdconfig/cgdconfig.8        Fri Aug 12 10:48:44 2022 +0000
+++ b/sbin/cgdconfig/cgdconfig.8        Fri Aug 12 10:49:17 2022 +0000
@@ -1,4 +1,4 @@
-.\" $NetBSD: cgdconfig.8,v 1.54 2022/08/12 10:48:44 riastradh Exp $
+.\" $NetBSD: cgdconfig.8,v 1.55 2022/08/12 10:49:17 riastradh Exp $
 .\"
 .\" Copyright (c) 2002, The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -395,6 +395,46 @@
 Should be the most recent version, currently
 .Dv 19 .
 Only used for argon2id.
+.It shared Ar name No algorithm Ar kdf No subkey Ar info
+Makes the key generation take an extra step to derive a subkey from the
+main key using the key derivation function
+.Ar kdf
+with input
+.Ar info .
+.Pp
+This enables a single password entry, for example, to decrypt multiple
+disks that use different keys, each derived as a subkey from the main
+key generated from the password.
+.Bl -tag -width 6n
+.It Ar name
+A string used to identify the same main key generation shared between
+parameters files for different disks listed in a single
+.Pa cgd.conf
+configuration file.
+.It Ar kdf
+The name of a key derivation function used to derive a subkey from the
+main key.
+Supported values:
+.Bl -tag -width 6n -offset indent
+.It Li hkdf-hmac-sha256
+The HKDF-Expand function of RFC 5869, instantiated with SHA-256.
+.El
+.It Ar info
+A base64 length-encoded string to distinguish different subkeys derived
+from a shared main key.
+Need not be secret.
+For example, it could be a nickname, or the disk's World-Wide Name, or
+a UUID generated for the disk, or just a random string.
+.El
+.Pp
+It is an error to reuse a shared key
+.Ar name
+with different keygen blocks, other than the
+.Ar info
+parameter,
+between parameters files used by a single
+.Pa cgd.conf
+configuration file.
 .El
 .Sh FILES
 .Bl -tag -width indentxxxxxxxxxxxxxxxxxx -compact
@@ -474,6 +514,45 @@
                             ly2TdxkFqOkYYcbyUKu/f60L;
 .Ed
 .Pp
+An example pair of configuration files which use shared keys so they
+can be derived from a single passphrase entry, with the 64-bit
+World-Wide Name of each disk (base64 length-encoded) as its subkey
+info:
+.Bl -tag -offset indent -width 6n
+.It Pa /etc/cgd/wd0a
+.Bd -literal
+algorithm       adiantum;
+iv-method       encblkno1;
+keylength       256;
+verify_method  gpt;
+keygen argon2id {
+        iterations 32;
+        memory 5214;
+        parallelism 2;
+        version 19;
+        salt AAAAgLZ5QgleU2m/Ib6wiPYxz98=;
+        shared "my laptop" algorithm hkdf-hmac-sha256 \e
+            subkey AAAAQEGELNr3bj3I;
+};
+.Ed
+.It Pa /etc/cgd/wd1a
+.Bd -literal
+algorithm       adiantum;
+iv-method       encblkno1;
+keylength       256;
+verify_method  gpt;
+keygen argon2id {
+        iterations 32;
+        memory 5214;
+        parallelism 2;
+        version 19;
+        salt AAAAgLZ5QgleU2m/Ib6wiPYxz98=;
+        shared "my laptop" algorithm hkdf-hmac-sha256 \e
+            subkey AAAAQHSC15pr1Pe4;
+};
+.Ed
+.El
+.Pp
 An example
 .Pa /etc/cgd/cgd.conf :
 .Bd -literal
@@ -517,6 +596,15 @@
 .%I University of Luxembourg
 .%U https://www.password-hashing.net/
 .Re
+.Rs
+.%A H. Krawczyk
+.%A P. Eronen
+.%T HMAC-based Extract-and-Expand Key Derivation Function (HKDF)
+.%I Internet Engineering Task Force
+.%U https://www.rfc-editor.org/rfc/rfc5869.html
+.%N RFC 5869
+.%D May 2010
+.Re
 .Pp
 .Dq PKCS #5 v2.0: Password-Based Cryptography Standard ,
 RSA Laboratories, March 25, 1999.
@@ -526,8 +614,9 @@
 utility appeared in
 .Nx 2.0 .
 .Pp
+Support for
 .Li argon2id
-support appeared in
+and for shared keys appeared in
 .Nx 10.0 .
 .Sh BUGS
 Pass phrases are limited to 1023 bytes.
diff -r 8b736c4d92ce -r 8b4e7542c555 sbin/cgdconfig/cgdconfig.c
--- a/sbin/cgdconfig/cgdconfig.c        Fri Aug 12 10:48:44 2022 +0000
+++ b/sbin/cgdconfig/cgdconfig.c        Fri Aug 12 10:49:17 2022 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: cgdconfig.c,v 1.55 2022/08/12 10:48:44 riastradh Exp $ */
+/* $NetBSD: cgdconfig.c,v 1.56 2022/08/12 10:49:17 riastradh Exp $ */
 
 /*-
  * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
@@ -33,12 +33,13 @@
 #ifndef lint
 __COPYRIGHT("@(#) Copyright (c) 2002, 2003\
  The NetBSD Foundation, Inc.  All rights reserved.");
-__RCSID("$NetBSD: cgdconfig.c,v 1.55 2022/08/12 10:48:44 riastradh Exp $");
+__RCSID("$NetBSD: cgdconfig.c,v 1.56 2022/08/12 10:49:17 riastradh Exp $");
 #endif
 
 #ifdef HAVE_ARGON2
 #include <argon2.h>
 #endif
+#include <assert.h>
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -66,6 +67,7 @@
 #include <sys/resource.h>
 #include <sys/statvfs.h>
 #include <sys/bitops.h>
+#include <sys/queue.h>
 
 #include <dev/cgdvar.h>
 
@@ -76,6 +78,7 @@
 #include "utils.h"
 #include "cgdconfig.h"
 #include "prog_ops.h"
+#include "hkdf_hmac_sha256.h"
 
 #define CGDCONFIG_CFILE                CGDCONFIG_DIR "/cgd.conf"
 
@@ -105,6 +108,19 @@
 #define        PFLAG_STDIN             0x04
 int    pflag = PFLAG_GETPASS;
 
+/*
+ * When configuring all cgds, save a cache of shared keys for key
+ * derivation.
+ */
+
+struct sharedkey {
+       int                      alg;
+       string_t                *id;
+       bits_t                  *key;
+       LIST_ENTRY(sharedkey)    list;
+};
+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 *);
@@ -216,6 +232,8 @@
        const char      *outfile = NULL;
 
        setprogname(*argv);
+       if (hkdf_hmac_sha256_selftest())
+               err(EXIT_FAILURE, "Crypto self-test failed");
        eliminate_cores();
        if (mlockall(MCL_FUTURE))
                err(EXIT_FAILURE, "Can't lock memory");
@@ -348,13 +366,69 @@
 }
 
 static bits_t *
-getkey(const char *dev, struct keygen *kg, size_t len)
+getsubkey_hkdf_hmac_sha256(bits_t *key, bits_t *info, size_t subkeylen)
+{
+       bits_t          *ret = NULL;
+       uint8_t         *tmp;
+
+       tmp = emalloc(BITS2BYTES(subkeylen));
+       if (hkdf_hmac_sha256(tmp, BITS2BYTES(subkeylen),
+               bits_getbuf(key), BITS2BYTES(bits_len(key)),
+               bits_getbuf(info), BITS2BYTES(bits_len(info)))) {
+               warnx("failed to derive HKDF-HMAC-SHA256 subkey");
+               goto out;
+       }
+
+       ret = bits_new(tmp, subkeylen);
+
+out:   free(tmp);
+       return ret;
+}
+
+static bits_t *
+getsubkey(int alg, bits_t *key, bits_t *info, size_t subkeylen)
+{
+
+       switch (alg) {
+       case SHARED_ALG_HKDF_HMAC_SHA256:
+               return getsubkey_hkdf_hmac_sha256(key, info, subkeylen);
+       default:
+               warnx("unrecognised shared key derivation method %d", alg);
+               return NULL;
+       }
+}
+
+static bits_t *
+getkey(const char *dev, struct keygen *kg, size_t len0)
 {
        bits_t  *ret = NULL;
        bits_t  *tmp;
 
-       VPRINTF(3, ("getkey(\"%s\", %p, %zu) called\n", dev, kg, len));
+       VPRINTF(3, ("getkey(\"%s\", %p, %zu) called\n", dev, kg, len0));
        for (; kg; kg=kg->next) {
+               struct sharedkey *sk = NULL;
+               size_t len = len0;
+
+               /*
+                * If shared, determine the shared key's length and
+                * probe the cache of shared keys.
+                */
+               if (kg->kg_sharedid) {
+                       const char *id = string_tocharstar(kg->kg_sharedid);
+
+                       len = kg->kg_sharedlen;
+                       LIST_FOREACH(sk, &sharedkeys, list) {
+                               if (kg->kg_sharedalg == sk->alg &&
+                                   kg->kg_sharedlen == bits_len(sk->key) &&
+                                   strcmp(id, string_tocharstar(sk->id)) == 0)
+                                       break;
+                       }
+                       if (sk) {
+                               tmp = sk->key;
+                               goto derive;
+                       }
+               }



Home | Main Index | Thread Index | Old Index