]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Encryption patch follow-up
authorTom Caputi <tcaputi@datto.com>
Tue, 12 Sep 2017 20:15:11 +0000 (16:15 -0400)
committerTom Caputi <tcaputi@datto.com>
Wed, 11 Oct 2017 20:54:48 +0000 (16:54 -0400)
* PBKDF2 implementation changed to OpenSSL implementation.

* HKDF implementation moved to its own file and tests
  added to ensure correctness.

* Removed libzfs's now unnecessary dependency on libzpool
  and libicp.

* Ztest can now create and test encrypted datasets. This is
  currently disabled until issue #6526 is resolved, but
  otherwise functions as advertised.

* Several small bug fixes discovered after enabling ztest
  to run on encrypted datasets.

* Fixed coverity defects added by the encryption patch.

* Updated man pages for encrypted send / receive behavior.

* Fixed a bug where encrypted datasets could receive
  DRR_WRITE_EMBEDDED records.

* Minor code cleanups / consolidation.

Signed-off-by: Tom Caputi <tcaputi@datto.com>
41 files changed:
COPYRIGHT
cmd/zfs/zfs_main.c
cmd/zpool/zpool_main.c
cmd/ztest/ztest.c
config/user-libssl.m4 [new file with mode: 0644]
config/user.m4
configure.ac
contrib/dracut/90zfs/module-setup.sh.in
include/sys/Makefile.am
include/sys/hkdf.h [new file with mode: 0644]
include/sys/zio_crypt.h
lib/libzfs/Makefile.am
lib/libzfs/THIRDPARTYLICENSE.openssl [new file with mode: 0644]
lib/libzfs/THIRDPARTYLICENSE.openssl.descrip [new file with mode: 0644]
lib/libzfs/libzfs_crypto.c
lib/libzfs/libzfs_sendrecv.c
lib/libzpool/Makefile.am
man/man8/zfs.8
module/icp/algs/sha2/sha2.c
module/zfs/Makefile.in
module/zfs/arc.c
module/zfs/dbuf.c
module/zfs/dmu_objset.c
module/zfs/dmu_send.c
module/zfs/dsl_crypt.c
module/zfs/hkdf.c [new file with mode: 0644]
module/zfs/zil.c
module/zfs/zio.c
module/zfs/zio_crypt.c
rpm/generic/zfs.spec.in
scripts/zloop.sh
tests/runfiles/linux.run
tests/zfs-tests/include/commands.cfg
tests/zfs-tests/tests/functional/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zpool_create/zpool_create_008_pos.ksh
tests/zfs-tests/tests/functional/hkdf/.gitignore [new file with mode: 0644]
tests/zfs-tests/tests/functional/hkdf/Makefile.am [new file with mode: 0644]
tests/zfs-tests/tests/functional/hkdf/cleanup.ksh [new file with mode: 0755]
tests/zfs-tests/tests/functional/hkdf/hkdf_test.c [new file with mode: 0644]
tests/zfs-tests/tests/functional/hkdf/run_hkdf_test.ksh [new file with mode: 0755]
tests/zfs-tests/tests/functional/hkdf/setup.ksh [new file with mode: 0755]

index 6d01ffaa752ba9346d941923ebdc74b9d72f2e46..9b94f927fe1b5eb781829ca6b55a58c4e8fed58f 100644 (file)
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -23,7 +23,17 @@ approved for release under LLNL-CODE-403049.
 
 Unless otherwise noted, all files in this distribution are released
 under the Common Development and Distribution License (CDDL).
-Exceptions are noted within the associated source files.  See the file
-OPENSOLARIS.LICENSE for more information.
+Exceptions are noted within the associated source files.  A few notable
+exceptions and their respective licenses include:
+
+Skein Checksum Implementation: module/icp/algs/skein/THIRDPARTYLICENSE
+AES Implementation: module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman
+AES Implementation: module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl
+PBKDF2 Implementation: lib/libzfs/THIRDPARTYLICENSE.openssl
+
+This product includes software developed by the OpenSSL Project for use
+in the OpenSSL Toolkit (http://www.openssl.org/)
+
+See the file OPENSOLARIS.LICENSE for more information.
 
 Refer to the git commit log for authoritative copyright attribution.
index ef131f9b5f1c7f8b850b2c382cc980be984c413f..b9b53f22b5c9e31a3d3f5ea027dcbc2b36311505 100644 (file)
@@ -7267,28 +7267,27 @@ zfs_do_change_key(int argc, char **argv)
                keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
                if (keystatus != ZFS_KEYSTATUS_AVAILABLE) {
                        ret = zfs_crypto_load_key(zhp, B_FALSE, NULL);
-                       if (ret != 0)
-                               goto error;
+                       if (ret != 0) {
+                               nvlist_free(props);
+                               zfs_close(zhp);
+                               return (-1);
+                       }
                }
 
-               /* refresh the properties so the new keystatus is visable */
+               /* refresh the properties so the new keystatus is visible */
                zfs_refresh_properties(zhp);
        }
 
        ret = zfs_crypto_rewrap(zhp, props, inheritkey);
-       if (ret != 0)
-               goto error;
+       if (ret != 0) {
+               nvlist_free(props);
+               zfs_close(zhp);
+               return (-1);
+       }
 
        nvlist_free(props);
        zfs_close(zhp);
        return (0);
-
-error:
-       if (props != NULL)
-               nvlist_free(props);
-       if (zhp != NULL)
-               zfs_close(zhp);
-       return (-1);
 }
 
 int
index 3ffd13c8ead3b452d0ffa72caf4110730b1d2bb4..c61639cc24a77ddad73097ce840f70c0f6ef7ff9 100644 (file)
@@ -8049,10 +8049,17 @@ main(int argc, char **argv)
                 * 'freeze' is a vile debugging abomination, so we treat
                 * it as such.
                 */
-               char buf[16384];
-               int fd = open(ZFS_DEV, O_RDWR);
-               (void) strlcpy((void *)buf, argv[2], sizeof (buf));
-               return (!!ioctl(fd, ZFS_IOC_POOL_FREEZE, buf));
+               zfs_cmd_t zc = {"\0"};
+
+               (void) strlcpy(zc.zc_name, argv[2], sizeof (zc.zc_name));
+               ret = zfs_ioctl(g_zfs, ZFS_IOC_POOL_FREEZE, &zc);
+               if (ret != 0) {
+                       (void) fprintf(stderr,
+                       gettext("failed to freeze pool: %d\n"), errno);
+                       ret = 1;
+               }
+
+               log_history = 0;
        } else {
                (void) fprintf(stderr, gettext("unrecognized "
                    "command '%s'\n"), cmdname);
index 5aa0c85f3b0d41b780120e97682e1ce4b70eef4a..d397d7279ed1f0ca19b12af2c33f7895c6944cff 100644 (file)
@@ -201,6 +201,7 @@ extern int  zfs_abd_scatter_enabled;
 
 static ztest_shared_opts_t *ztest_shared_opts;
 static ztest_shared_opts_t ztest_opts;
+static char *ztest_wkeydata = "abcdefghijklmnopqrstuvwxyz012345";
 
 typedef struct ztest_shared_ds {
        uint64_t        zd_seq;
@@ -1180,6 +1181,42 @@ ztest_spa_prop_set_uint64(zpool_prop_t prop, uint64_t value)
        return (error);
 }
 
+static int
+ztest_dmu_objset_own(const char *name, dmu_objset_type_t type,
+    boolean_t readonly, boolean_t decrypt, void *tag, objset_t **osp)
+{
+       int err;
+
+       err = dmu_objset_own(name, type, readonly, decrypt, tag, osp);
+       if (decrypt && err == EACCES) {
+               char ddname[ZFS_MAX_DATASET_NAME_LEN];
+               dsl_crypto_params_t *dcp;
+               nvlist_t *crypto_args = fnvlist_alloc();
+               char *cp = NULL;
+
+               /* spa_keystore_load_wkey() expects a dsl dir name */
+               strcpy(ddname, name);
+               cp = strchr(ddname, '@');
+               if (cp != NULL)
+                       *cp = '\0';
+
+               fnvlist_add_uint8_array(crypto_args, "wkeydata",
+                   (uint8_t *)ztest_wkeydata, WRAPPING_KEY_LEN);
+               VERIFY0(dsl_crypto_params_create_nvlist(DCP_CMD_NONE, NULL,
+                   crypto_args, &dcp));
+               err = spa_keystore_load_wkey(ddname, dcp, B_FALSE);
+               dsl_crypto_params_free(dcp, B_FALSE);
+               fnvlist_free(crypto_args);
+
+               if (err != 0)
+                       return (err);
+
+               err = dmu_objset_own(name, type, readonly, decrypt, tag, osp);
+       }
+
+       return (err);
+}
+
 
 /*
  * Object and range lock mechanics
@@ -3532,11 +3569,57 @@ ztest_objset_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
 static int
 ztest_dataset_create(char *dsname)
 {
-       uint64_t zilset = ztest_random(100);
-       int err = dmu_objset_create(dsname, DMU_OST_OTHER, 0, NULL,
+       int err;
+       uint64_t rand;
+       dsl_crypto_params_t *dcp = NULL;
+
+       /*
+        * 50% of the time, we create encrypted datasets
+        * using a random cipher suite and a hard-coded
+        * wrapping key.
+        */
+       rand = ztest_random(2);
+       if (rand != 0) {
+               nvlist_t *crypto_args = fnvlist_alloc();
+               nvlist_t *props = fnvlist_alloc();
+
+               /* slight bias towards the default cipher suite */
+               rand = ztest_random(ZIO_CRYPT_FUNCTIONS);
+               if (rand < ZIO_CRYPT_AES_128_CCM)
+                       rand = ZIO_CRYPT_ON;
+
+               fnvlist_add_uint64(props,
+                   zfs_prop_to_name(ZFS_PROP_ENCRYPTION), rand);
+               fnvlist_add_uint8_array(crypto_args, "wkeydata",
+                   (uint8_t *)ztest_wkeydata, WRAPPING_KEY_LEN);
+
+               /*
+                * These parameters aren't really used by the kernel. They
+                * are simply stored so that userspace knows how to load
+                * the wrapping key.
+                */
+               fnvlist_add_uint64(props,
+                   zfs_prop_to_name(ZFS_PROP_KEYFORMAT), ZFS_KEYFORMAT_RAW);
+               fnvlist_add_string(props,
+                   zfs_prop_to_name(ZFS_PROP_KEYLOCATION), "prompt");
+               fnvlist_add_uint64(props,
+                   zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), 0ULL);
+               fnvlist_add_uint64(props,
+                   zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), 0ULL);
+
+               VERIFY0(dsl_crypto_params_create_nvlist(DCP_CMD_NONE, props,
+                   crypto_args, &dcp));
+
+               fnvlist_free(crypto_args);
+               fnvlist_free(props);
+       }
+
+       err = dmu_objset_create(dsname, DMU_OST_OTHER, 0, dcp,
            ztest_objset_create_cb, NULL);
+       dsl_crypto_params_free(dcp, !!err);
 
-       if (err || zilset < 80)
+       rand = ztest_random(100);
+       if (err || rand < 80)
                return (err);
 
        if (ztest_opts.zo_verbose >= 5)
@@ -3556,7 +3639,8 @@ ztest_objset_destroy_cb(const char *name, void *arg)
        /*
         * Verify that the dataset contains a directory object.
         */
-       VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_TRUE, B_TRUE, FTAG, &os));
+       VERIFY0(ztest_dmu_objset_own(name, DMU_OST_OTHER, B_TRUE,
+           B_TRUE, FTAG, &os));
        error = dmu_object_info(os, ZTEST_DIROBJ, &doi);
        if (error != ENOENT) {
                /* We could have crashed in the middle of destroying it */
@@ -3640,11 +3724,12 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
         * (invoked from ztest_objset_destroy_cb()) should just throw it away.
         */
        if (ztest_random(2) == 0 &&
-           dmu_objset_own(name, DMU_OST_OTHER, B_FALSE,
+           ztest_dmu_objset_own(name, DMU_OST_OTHER, B_FALSE,
            B_TRUE, FTAG, &os) == 0) {
                ztest_zd_init(zdtmp, NULL, os);
                zil_replay(os, zdtmp, ztest_replay_vector);
                ztest_zd_fini(zdtmp);
+               txg_wait_synced(dmu_objset_pool(os), 0);
                dmu_objset_disown(os, B_TRUE, FTAG);
        }
 
@@ -3659,8 +3744,8 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
        /*
         * Verify that the destroyed dataset is no longer in the namespace.
         */
-       VERIFY3U(ENOENT, ==, dmu_objset_own(name, DMU_OST_OTHER, B_TRUE, B_TRUE,
-           FTAG, &os));
+       VERIFY3U(ENOENT, ==, ztest_dmu_objset_own(name, DMU_OST_OTHER, B_TRUE,
+           B_TRUE, FTAG, &os));
 
        /*
         * Verify that we can create a new dataset.
@@ -3674,7 +3759,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
                fatal(0, "dmu_objset_create(%s) = %d", name, error);
        }
 
-       VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, B_TRUE,
+       VERIFY0(ztest_dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, B_TRUE,
            FTAG, &os));
 
        ztest_zd_init(zdtmp, NULL, os);
@@ -3710,10 +3795,11 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
        /*
         * Verify that we cannot own an objset that is already owned.
         */
-       VERIFY3U(EBUSY, ==,
-           dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, B_TRUE, FTAG, &os2));
+       VERIFY3U(EBUSY, ==, ztest_dmu_objset_own(name, DMU_OST_OTHER,
+           B_FALSE, B_TRUE, FTAG, &os2));
 
        zil_close(zilog);
+       txg_wait_synced(spa_get_dsl(os->os_spa), 0);
        dmu_objset_disown(os, B_TRUE, FTAG);
        ztest_zd_fini(zdtmp);
 out:
@@ -3868,7 +3954,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
                fatal(0, "dmu_objset_create(%s) = %d", clone2name, error);
        }
 
-       error = dmu_objset_own(snap2name, DMU_OST_ANY, B_TRUE, B_TRUE,
+       error = ztest_dmu_objset_own(snap2name, DMU_OST_ANY, B_TRUE, B_TRUE,
            FTAG, &os);
        if (error)
                fatal(0, "dmu_objset_own(%s) = %d", snap2name, error);
@@ -6262,7 +6348,8 @@ ztest_dataset_open(int d)
        }
        ASSERT(error == 0 || error == EEXIST);
 
-       VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, B_TRUE, zd, &os));
+       VERIFY0(ztest_dmu_objset_own(name, DMU_OST_OTHER, B_FALSE,
+           B_TRUE, zd, &os));
        (void) rw_unlock(&ztest_name_lock);
 
        ztest_zd_init(zd, ZTEST_GET_SHARED_DS(d), os);
@@ -6303,6 +6390,7 @@ ztest_dataset_close(int d)
        ztest_ds_t *zd = &ztest_ds[d];
 
        zil_close(zd->zd_zilog);
+       txg_wait_synced(spa_get_dsl(zd->zd_os->os_spa), 0);
        dmu_objset_disown(zd->zd_os, B_TRUE, zd);
 
        ztest_zd_fini(zd);
@@ -6355,7 +6443,7 @@ ztest_run(ztest_shared_t *zs)
        ztest_spa = spa;
 
        dmu_objset_stats_t dds;
-       VERIFY0(dmu_objset_own(ztest_opts.zo_pool,
+       VERIFY0(ztest_dmu_objset_own(ztest_opts.zo_pool,
            DMU_OST_ANY, B_TRUE, B_TRUE, FTAG, &os));
        dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
        dmu_objset_fast_stat(os, &dds);
@@ -6582,11 +6670,10 @@ ztest_freeze(void)
        VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG));
        ASSERT(spa_freeze_txg(spa) == UINT64_MAX);
        VERIFY3U(0, ==, ztest_dataset_open(0));
-       ztest_dataset_close(0);
-
        spa->spa_debug = B_TRUE;
        ztest_spa = spa;
        txg_wait_synced(spa_get_dsl(spa), 0);
+       ztest_dataset_close(0);
        ztest_reguid(NULL, 0);
 
        spa_close(spa, FTAG);
diff --git a/config/user-libssl.m4 b/config/user-libssl.m4
new file mode 100644 (file)
index 0000000..b65a514
--- /dev/null
@@ -0,0 +1,12 @@
+dnl #
+dnl # Check for libssl. Used for userspace password derivation via PBKDF2.
+dnl #
+AC_DEFUN([ZFS_AC_CONFIG_USER_LIBSSL], [
+       LIBSSL=
+
+       AC_CHECK_HEADER([openssl/evp.h], [], [AC_MSG_FAILURE([
+        *** evp.h missing, libssl-devel package required])])
+
+       AC_SUBST([LIBSSL], ["-lssl -lcrypto"])
+       AC_DEFINE([HAVE_LIBSSL], 1, [Define if you have libssl])
+])
index 21ff7143a56b0ffa5d821acb94f35691a286233e..73f6433a2ea7a498a125145993d68bc21de2d06b 100644 (file)
@@ -13,6 +13,7 @@ AC_DEFUN([ZFS_AC_CONFIG_USER], [
        ZFS_AC_CONFIG_USER_LIBBLKID
        ZFS_AC_CONFIG_USER_LIBATTR
        ZFS_AC_CONFIG_USER_LIBUDEV
+       ZFS_AC_CONFIG_USER_LIBSSL
        ZFS_AC_CONFIG_USER_FRAME_LARGER_THAN
        ZFS_AC_CONFIG_USER_RUNSTATEDIR
        ZFS_AC_CONFIG_USER_MAKEDEV_IN_SYSMACROS
index 8d1a8c9f18bd56a2d073cc48018e8a33880ea0b2..6dff03092af589983ec07f58bb854f77d0731cf0 100644 (file)
@@ -248,6 +248,7 @@ AC_CONFIG_FILES([
        tests/zfs-tests/tests/functional/grow_pool/Makefile
        tests/zfs-tests/tests/functional/grow_replicas/Makefile
        tests/zfs-tests/tests/functional/history/Makefile
+       tests/zfs-tests/tests/functional/hkdf/Makefile
        tests/zfs-tests/tests/functional/inheritance/Makefile
        tests/zfs-tests/tests/functional/inuse/Makefile
        tests/zfs-tests/tests/functional/large_files/Makefile
index 71c81639b2ff2a23bea8abfd4ed411c3f12256ff..0f85c2a2505fc9ef8bfa156407fdbd1aa8133069 100755 (executable)
@@ -25,6 +25,7 @@ installkernel() {
        instmods znvpair
        instmods zavl
        instmods zunicode
+       instmods icp
        instmods spl
        instmods zlib_deflate
        instmods zlib_inflate
index 22b647a1eb6506c552ddeaffb326454205f3532b..d3835ca29f5aa610709375d3c867b0a03b218933 100644 (file)
@@ -35,6 +35,7 @@ COMMON_H = \
        $(top_srcdir)/include/sys/dsl_userhold.h \
        $(top_srcdir)/include/sys/edonr.h \
        $(top_srcdir)/include/sys/efi_partition.h \
+       $(top_srcdir)/include/sys/hkdf.h \
        $(top_srcdir)/include/sys/metaslab.h \
        $(top_srcdir)/include/sys/metaslab_impl.h \
        $(top_srcdir)/include/sys/mmp.h \
diff --git a/include/sys/hkdf.h b/include/sys/hkdf.h
new file mode 100644 (file)
index 0000000..e0f7678
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2017, Datto, Inc. All rights reserved.
+ */
+
+#ifndef        _SYS_HKDF_H_
+#define        _SYS_HKDF_H_
+
+#include <sys/types.h>
+
+int hkdf_sha512(uint8_t *key_material, uint_t km_len, uint8_t *salt,
+    uint_t salt_len, uint8_t *info, uint_t info_len, uint8_t *output_key,
+    uint_t out_len);
+
+#endif /* _SYS_HKDF_H_ */
index 9ddfe4280ace205afd4b17bf76659d635604251b..9cf9a17c2c1368ec883921f51c1a5ba012a0aa00 100644 (file)
@@ -32,18 +32,9 @@ struct zbookmark_phys;
 
 #define        WRAPPING_KEY_LEN        32
 #define        WRAPPING_IV_LEN         ZIO_DATA_IV_LEN
-#define        WRAPPING_MAC_LEN        16
-
-#define        SHA1_DIGEST_LEN         20
-#define        SHA512_DIGEST_LEN       64
-#define        SHA512_HMAC_KEYLEN      64
-
+#define        WRAPPING_MAC_LEN        ZIO_DATA_MAC_LEN
 #define        MASTER_KEY_MAX_LEN      32
-#define        L2ARC_DEFAULT_CRYPT ZIO_CRYPT_AES_256_CCM
-
-/* utility macros */
-#define        BITS_TO_BYTES(x) ((x + NBBY - 1) / NBBY)
-#define        BYTES_TO_BITS(x) (x * NBBY)
+#define        SHA512_HMAC_KEYLEN      64
 
 typedef enum zio_crypt_type {
        ZC_TYPE_NONE = 0,
@@ -133,7 +124,7 @@ int zio_crypt_do_indirect_mac_checksum(boolean_t generate, void *buf,
 int zio_crypt_do_indirect_mac_checksum_abd(boolean_t generate, abd_t *abd,
     uint_t datalen, boolean_t byteswap, uint8_t *cksum);
 int zio_crypt_do_hmac(zio_crypt_key_t *key, uint8_t *data, uint_t datalen,
-    uint8_t *digestbuf);
+    uint8_t *digestbuf, uint_t digestlen);
 int zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen,
     boolean_t byteswap, uint8_t *portable_mac, uint8_t *local_mac);
 int zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt,
index 7389f002d20d655c0b34faafe3795d3eb4cfeae2..e5b2ce765d1e74099b01bbcfa0de5c49ca46efb1 100644 (file)
@@ -30,6 +30,7 @@ USER_C = \
        libzfs_util.c
 
 KERNEL_C = \
+       algs/sha2/sha2.c \
        zfeature_common.c \
        zfs_comutil.c \
        zfs_deleg.c \
@@ -52,15 +53,13 @@ nodist_libzfs_la_SOURCES = \
 
 libzfs_la_LIBADD = \
         $(top_builddir)/lib/libefi/libefi.la \
-       $(top_builddir)/lib/libicp/libicp.la \
        $(top_builddir)/lib/libnvpair/libnvpair.la \
        $(top_builddir)/lib/libshare/libshare.la \
        $(top_builddir)/lib/libtpool/libtpool.la \
        $(top_builddir)/lib/libuutil/libuutil.la \
-       $(top_builddir)/lib/libzpool/libzpool.la \
        $(top_builddir)/lib/libzfs_core/libzfs_core.la
 
-libzfs_la_LIBADD += -lm $(LIBBLKID) $(LIBUDEV)
+libzfs_la_LIBADD += -lm $(LIBBLKID) $(LIBUDEV) $(LIBSSL)
 libzfs_la_LDFLAGS = -version-info 2:0:0
 
 EXTRA_DIST = $(libzfs_pc_DATA) $(USER_C)
diff --git a/lib/libzfs/THIRDPARTYLICENSE.openssl b/lib/libzfs/THIRDPARTYLICENSE.openssl
new file mode 100644 (file)
index 0000000..a2c4adc
--- /dev/null
@@ -0,0 +1,127 @@
+
+  LICENSE ISSUES
+  ==============
+
+  The OpenSSL toolkit stays under a dual license, i.e. both the conditions of
+  the OpenSSL License and the original SSLeay license apply to the toolkit.
+  See below for the actual license texts. Actually both licenses are BSD-style
+  Open Source licenses. In case of any license issues related to OpenSSL
+  please contact openssl-core@openssl.org.
+
+  OpenSSL License
+  ---------------
+
+/* ====================================================================
+ * Copyright (c) 1998-2008 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+ Original SSLeay License
+ -----------------------
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
diff --git a/lib/libzfs/THIRDPARTYLICENSE.openssl.descrip b/lib/libzfs/THIRDPARTYLICENSE.openssl.descrip
new file mode 100644 (file)
index 0000000..a9125ea
--- /dev/null
@@ -0,0 +1 @@
+PBKDF2 IMPLEMENTATION
index 8bd7880745ce6a9047b30dc0c9b69fa2e7c2e509..b1fac2f6213856321c500e5ee1ac09d768dcb5fe 100644 (file)
 #include <sys/zfs_context.h>
 #include <sys/fs/zfs.h>
 #include <sys/dsl_crypt.h>
-#include <sys/crypto/icp.h>
 #include <libintl.h>
 #include <termios.h>
 #include <signal.h>
 #include <errno.h>
+#include <openssl/evp.h>
 #include <libzfs.h>
 #include "libzfs_impl.h"
 #include "zfeature_common.h"
@@ -437,139 +437,6 @@ error:
        return (ret);
 }
 
-static int
-pbkdf2(uint8_t *passphrase, size_t passphraselen, uint8_t *salt,
-    size_t saltlen, uint64_t iterations, uint8_t *output,
-    size_t outputlen)
-{
-       int ret;
-       uint64_t iter;
-       uint32_t blockptr, i;
-       uint16_t hmac_key_len;
-       uint8_t *hmac_key;
-       uint8_t block[SHA1_DIGEST_LEN * 2];
-       uint8_t *hmacresult = block + SHA1_DIGEST_LEN;
-       crypto_mechanism_t mech;
-       crypto_key_t key;
-       crypto_data_t in_data, out_data;
-       crypto_ctx_template_t tmpl = NULL;
-
-       /* initialize output */
-       memset(output, 0, outputlen);
-
-       /* initialize icp for use */
-       icp_init();
-
-       /* HMAC key size is max(sizeof(uint32_t) + salt len, sha 256 len) */
-       if (saltlen > SHA1_DIGEST_LEN) {
-               hmac_key_len = saltlen + sizeof (uint32_t);
-       } else {
-               hmac_key_len = SHA1_DIGEST_LEN;
-       }
-
-       hmac_key = calloc(hmac_key_len, 1);
-       if (!hmac_key) {
-               ret = ENOMEM;
-               goto error;
-       }
-
-       /* initialize sha 256 hmac mechanism */
-       mech.cm_type = crypto_mech2id(SUN_CKM_SHA1_HMAC);
-       mech.cm_param = NULL;
-       mech.cm_param_len = 0;
-
-       /* initialize passphrase as a crypto key */
-       key.ck_format = CRYPTO_KEY_RAW;
-       key.ck_length = BYTES_TO_BITS(passphraselen);
-       key.ck_data = passphrase;
-
-       /*
-        * initialize crypto data for the input data. length will change
-        * after the first iteration, so we will initialize it in the loop.
-        */
-       in_data.cd_format = CRYPTO_DATA_RAW;
-       in_data.cd_offset = 0;
-       in_data.cd_raw.iov_base = (char *)hmac_key;
-
-       /* initialize crypto data for the output data */
-       out_data.cd_format = CRYPTO_DATA_RAW;
-       out_data.cd_offset = 0;
-       out_data.cd_length = SHA1_DIGEST_LEN;
-       out_data.cd_raw.iov_base = (char *)hmacresult;
-       out_data.cd_raw.iov_len = out_data.cd_length;
-
-       /* initialize the context template */
-       ret = crypto_create_ctx_template(&mech, &key, &tmpl, KM_SLEEP);
-       if (ret != CRYPTO_SUCCESS) {
-               ret = EIO;
-               goto error;
-       }
-
-       /* main loop */
-       for (blockptr = 0; blockptr < outputlen; blockptr += SHA1_DIGEST_LEN) {
-
-               /*
-                * for the first iteration, the HMAC key is the user-provided
-                * salt concatenated with the block index (1-indexed)
-                */
-               i = htobe32(1 + (blockptr / SHA1_DIGEST_LEN));
-               memmove(hmac_key, salt, saltlen);
-               memmove(hmac_key + saltlen, (uint8_t *)(&i), sizeof (uint32_t));
-
-               /* block initializes to zeroes (no XOR) */
-               memset(block, 0, SHA1_DIGEST_LEN);
-
-               for (iter = 0; iter < iterations; iter++) {
-                       if (iter > 0) {
-                               in_data.cd_length = SHA1_DIGEST_LEN;
-                               in_data.cd_raw.iov_len = in_data.cd_length;
-                       } else {
-                               in_data.cd_length = saltlen + sizeof (uint32_t);
-                               in_data.cd_raw.iov_len = in_data.cd_length;
-                       }
-
-                       ret = crypto_mac(&mech, &in_data, &key, tmpl,
-                           &out_data, NULL);
-                       if (ret != CRYPTO_SUCCESS) {
-                               ret = EIO;
-                               goto error;
-                       }
-
-                       /* HMAC key now becomes the output of this iteration */
-                       memmove(hmac_key, hmacresult, SHA1_DIGEST_LEN);
-
-                       /* XOR this iteration's result with the current block */
-                       for (i = 0; i < SHA1_DIGEST_LEN; i++) {
-                               block[i] ^= hmacresult[i];
-                       }
-               }
-
-               /*
-                * compute length of this block, make sure we don't write
-                * beyond the end of the output, truncating if necessary
-                */
-               if (blockptr + SHA1_DIGEST_LEN > outputlen) {
-                       memmove(output + blockptr, block, outputlen - blockptr);
-               } else {
-                       memmove(output + blockptr, block, SHA1_DIGEST_LEN);
-               }
-       }
-
-       crypto_destroy_ctx_template(tmpl);
-       free(hmac_key);
-       icp_fini();
-
-       return (0);
-
-error:
-       crypto_destroy_ctx_template(tmpl);
-       if (hmac_key != NULL)
-               free(hmac_key);
-       icp_fini();
-
-       return (ret);
-}
-
 static int
 derive_key(libzfs_handle_t *hdl, zfs_keyformat_t format, uint64_t iters,
     uint8_t *key_material, size_t key_material_len, uint64_t salt,
@@ -599,10 +466,12 @@ derive_key(libzfs_handle_t *hdl, zfs_keyformat_t format, uint64_t iters,
                break;
        case ZFS_KEYFORMAT_PASSPHRASE:
                salt = LE_64(salt);
-               ret = pbkdf2(key_material, strlen((char *)key_material),
-                   ((uint8_t *)&salt), sizeof (uint64_t), iters,
-                   key, WRAPPING_KEY_LEN);
-               if (ret != 0) {
+
+               ret = PKCS5_PBKDF2_HMAC_SHA1((char *)key_material,
+                   strlen((char *)key_material), ((uint8_t *)&salt),
+                   sizeof (uint64_t), iters, WRAPPING_KEY_LEN, key);
+               if (ret != 1) {
+                       ret = EIO;
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "Failed to generate key from passphrase."));
                        goto error;
@@ -1207,9 +1076,13 @@ try_again:
        ret = lzc_load_key(zhp->zfs_name, noop, key_data, WRAPPING_KEY_LEN);
        if (ret != 0) {
                switch (ret) {
+               case EPERM:
+                       zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+                           "Permission denied."));
+                       break;
                case EINVAL:
                        zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
-                           "Invalid parameters provided for %s."),
+                           "Invalid parameters provided for dataset %s."),
                            zfs_get_name(zhp));
                        break;
                case EEXIST:
@@ -1318,6 +1191,10 @@ zfs_crypto_unload_key(zfs_handle_t *zhp)
 
        if (ret != 0) {
                switch (ret) {
+               case EPERM:
+                       zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+                           "Permission denied."));
+                       break;
                case ENOENT:
                        zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
                            "Key already unloaded for '%s'."),
@@ -1375,8 +1252,10 @@ zfs_crypto_verify_rewrap_nvlist(zfs_handle_t *zhp, nvlist_t *props,
        new_props = zfs_valid_proplist(zhp->zfs_hdl, zhp->zfs_type, props,
            zfs_prop_get_int(zhp, ZFS_PROP_ZONED), NULL, zhp->zpool_hdl,
            B_TRUE, errbuf);
-       if (new_props == NULL)
+       if (new_props == NULL) {
+               ret = EINVAL;
                goto error;
+       }
 
        *props_out = new_props;
        return (0);
@@ -1475,6 +1354,13 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
                                ret = nvlist_add_uint64(props,
                                    zfs_prop_to_name(ZFS_PROP_KEYFORMAT),
                                    keyformat);
+                               if (ret != 0) {
+                                       zfs_error_aux(zhp->zfs_hdl,
+                                           dgettext(TEXT_DOMAIN, "Failed to "
+                                           "get existing keyformat "
+                                           "property."));
+                                       goto error;
+                               }
                        }
 
                        if (keylocation == NULL) {
@@ -1578,6 +1464,10 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
        ret = lzc_change_key(zhp->zfs_name, cmd, props, wkeydata, wkeylen);
        if (ret != 0) {
                switch (ret) {
+               case EPERM:
+                       zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+                           "Permission denied."));
+                       break;
                case EINVAL:
                        zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
                            "Invalid properties for key change."));
index 855a297c99e21c7146c452ae0529749c1532112b..934da54e1c1ea69c9598f58a01b8e372194c05b8 100644 (file)
@@ -2657,6 +2657,13 @@ recv_fix_encryption_heirarchy(libzfs_handle_t *hdl, const char *destname,
        int err;
        nvpair_t *fselem = NULL;
        nvlist_t *stream_fss;
+       char *cp;
+       char top_zfs[ZFS_MAX_DATASET_NAME_LEN];
+
+       (void) strcpy(top_zfs, destname);
+       cp = strrchr(top_zfs, '@');
+       if (cp != NULL)
+               *cp = '\0';
 
        VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss", &stream_fss));
 
@@ -2758,9 +2765,11 @@ recv_fix_encryption_heirarchy(libzfs_handle_t *hdl, const char *destname,
                /*
                 * If the dataset is not flagged as an encryption root and is
                 * currently an encryption root, force it to inherit from its
-                * parent.
+                * parent. The root of a raw send should never be
+                * force-inherited.
                 */
-               if (!stream_encroot && is_encroot) {
+               if (!stream_encroot && is_encroot &&
+                   strcmp(top_zfs, fsname) != 0) {
                        err = lzc_change_key(fsname, DCP_CMD_FORCE_INHERIT,
                            NULL, NULL, 0);
                        if (err != 0) {
index 133f1539a0ec2f0a52c585531ec393004a631ef4..95e2493421a68b3d44ca26a5a5c9445c7e6a7820 100644 (file)
@@ -68,6 +68,7 @@ KERNEL_C = \
        dsl_destroy.c \
        dsl_userhold.c \
        edonr_zfs.c \
+       hkdf.c \
        fm.c \
        gzip.c \
        lzjb.c \
index 5b8f251d1cf9a1ca486d8bb92ce0629d0abb1f86..27c750d7e4585518ab3a6afc1732107493ba99f8 100644 (file)
@@ -2179,18 +2179,31 @@ and
 .Sy pbkdf2iters .
 After entering an encryption key, the
 created dataset will become an encryption root. Any descendant datasets will
-inherit their encryption key from the encryption root, meaning that loading,
-unloading, or changing the key for the encryption root will implicitly do the
-same for all inheriting datasets. If this inheritence is not desired, simply
-supply a new
-.Sy encryption
-and
+inherit their encryption key from the encryption root by default, meaning that
+loading, unloading, or changing the key for the encryption root will implicitly
+do the same for all inheriting datasets. If this inheritance is not desired,
+simply supply a
 .Sy keyformat
 when creating the child dataset or use
 .Nm zfs Cm change-key
-to break the relationship. The one exception is that clones will always use
-their origin's encryption key. Encryption root inheritence can be tracked via
-the read-only
+to break an existing relationship, creating a new encryption root on the child.
+Note that the child's
+.Sy keyformat
+may match that of the parent while still creating a new encryption root, and
+that changing the
+.Sy encryption
+property alone does not create a new encryption root; this would simply use a
+different cipher suite with the same key as its encryption root. The one
+exception is that clones will always use their origin's encryption key.
+As a result of this exception, some encryption-related properties (namely
+.Sy keystatus ,
+.Sy keyformat ,
+.Sy keylocation ,
+and
+.Sy pbkdf2iters )
+do not inherit like other ZFS properties and instead use the value determined
+by their encryption root. Encryption root inheritance can be tracked via the
+read-only
 .Sy encryptionroot
 property.
 .Pp
@@ -3165,7 +3178,10 @@ feature enabled.
 If the
 .Sy lz4_compress
 feature is active on the sending system, then the receiving system must have
-that feature enabled as well.
+that feature enabled as well. Datasets that are sent with this flag may not be
+received as an encrypted dataset, since encrypted datasets cannot use the
+.Sy embedded_data
+feature.
 See
 .Xr zpool-features 5
 for details on ZFS feature flags and the
@@ -3248,7 +3264,10 @@ Include the dataset's properties in the stream.
 This flag is implicit when
 .Fl R
 is specified.
-The receiving system must also support this feature.
+The receiving system must also support this feature. Sends of encrypted datasets
+must use
+.Fl w
+when using this flag.
 .It Fl v, -verbose
 Print verbose information about the stream package generated.
 This information includes a per-second report of how much data has been sent.
@@ -3339,8 +3358,10 @@ feature enabled.
 If the
 .Sy lz4_compress
 feature is active on the sending system, then the receiving system must have
-that feature enabled as well. Note that streams generated using this flag are
-unable to be received into an encrypted dataset.
+that feature enabled as well. Datasets that are sent with this flag may not be
+received as an encrypted dataset, since encrypted datasets cannot use the
+.Sy embedded_data
+feature.
 See
 .Xr zpool-features 5
 for details on ZFS feature flags and the
@@ -3463,6 +3484,15 @@ is a special case because, even if
 is a read-only property and cannot be set, it's allowed to receive the send
 stream as a clone of the given snapshot.
 .Pp
+Raw encrypted send streams (created with
+.Nm zfs Cm send Fl w
+) may only be received as is, and cannot be re-encrypted, decrypted, or
+recompressed by the receive process. Unencrypted streams can be received as
+encrypted datasets, either through inheritance or by specifying encryption
+parameters with the
+.Fl o
+options.
+.Pp
 The name of the snapshot
 .Pq and file system, if a full stream is received
 that this subcommand creates depends on the argument type and the use of the
index 6f7971afdbc038d93904507708ff4853d67148d4..05a2e6ad146a0be30f4618b63f822c134388d268 100644 (file)
@@ -52,7 +52,8 @@
 static void Encode(uint8_t *, uint32_t *, size_t);
 static void Encode64(uint8_t *, uint64_t *, size_t);
 
-#if    defined(__amd64)
+/* userspace only supports the generic version */
+#if    defined(__amd64) && defined(_KERNEL)
 #define        SHA512Transform(ctx, in) SHA512TransformBlocks((ctx), (in), 1)
 #define        SHA256Transform(ctx, in) SHA256TransformBlocks((ctx), (in), 1)
 
@@ -62,7 +63,7 @@ void SHA256TransformBlocks(SHA2_CTX *ctx, const void *in, size_t num);
 #else
 static void SHA256Transform(SHA2_CTX *, const uint8_t *);
 static void SHA512Transform(SHA2_CTX *, const uint8_t *);
-#endif /* __amd64 */
+#endif /* __amd64 && _KERNEL */
 
 static uint8_t PADDING[128] = { 0x80, /* all zeros */ };
 
@@ -142,7 +143,7 @@ static uint8_t PADDING[128] = { 0x80, /* all zeros */ };
 #endif /* _BIG_ENDIAN */
 
 
-#if    !defined(__amd64)
+#if    !defined(__amd64) || !defined(_KERNEL)
 /* SHA256 Transform */
 
 static void
@@ -600,7 +601,7 @@ SHA512Transform(SHA2_CTX *ctx, const uint8_t *blk)
        ctx->state.s64[7] += h;
 
 }
-#endif /* !__amd64 */
+#endif /* !__amd64 || !_KERNEL */
 
 
 /*
@@ -838,7 +839,7 @@ SHA2Update(SHA2_CTX *ctx, const void *inptr, size_t input_len)
                        i = buf_len;
                }
 
-#if !defined(__amd64)
+#if !defined(__amd64) || !defined(_KERNEL)
                if (algotype <= SHA256_HMAC_GEN_MECH_INFO_TYPE) {
                        for (; i + buf_limit - 1 < input_len; i += buf_limit) {
                                SHA256Transform(ctx, &input[i]);
@@ -866,7 +867,7 @@ SHA2Update(SHA2_CTX *ctx, const void *inptr, size_t input_len)
                                i += block_count << 7;
                        }
                }
-#endif /* !__amd64 */
+#endif /* !__amd64 || !_KERNEL */
 
                /*
                 * general optimization:
index 72f28a89df52eb2f47606e1b17b3777aec6792b3..606b0a47c8408560b9549cefd4eaf3e7278094f0 100644 (file)
@@ -41,6 +41,7 @@ $(MODULE)-objs += dsl_synctask.o
 $(MODULE)-objs += edonr_zfs.o
 $(MODULE)-objs += fm.o
 $(MODULE)-objs += gzip.o
+$(MODULE)-objs += hkdf.o
 $(MODULE)-objs += lzjb.o
 $(MODULE)-objs += lz4.o
 $(MODULE)-objs += metaslab.o
index 992e57ce61539544319f3b1b2dd5a1aac0aa9a32..1329e8e83c5b19a75cca40cee4cb0d3c47cc87b4 100644 (file)
@@ -6698,6 +6698,9 @@ arc_write_ready(zio_t *zio)
        HDR_SET_PSIZE(hdr, psize);
        arc_hdr_set_compress(hdr, compress);
 
+       if (zio->io_error != 0 || psize == 0)
+               goto out;
+
        /*
         * Fill the hdr with data. If the buffer is encrypted we have no choice
         * but to copy the data into b_radb. If the hdr is compressed, the data
@@ -6713,6 +6716,7 @@ arc_write_ready(zio_t *zio)
         * the data into it; otherwise, we share the data directly if we can.
         */
        if (ARC_BUF_ENCRYPTED(buf)) {
+               ASSERT3U(psize, >, 0);
                ASSERT(ARC_BUF_COMPRESSED(buf));
                arc_hdr_alloc_abd(hdr, B_TRUE);
                abd_copy(hdr->b_crypt_hdr.b_rabd, zio->io_abd, psize);
@@ -6745,6 +6749,7 @@ arc_write_ready(zio_t *zio)
                arc_share_buf(hdr, buf);
        }
 
+out:
        arc_hdr_verify(hdr, bp);
        spl_fstrans_unmark(cookie);
 }
@@ -8321,7 +8326,7 @@ l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, uint64_t asize,
        boolean_t bswap = (hdr->b_l1hdr.b_byteswap != DMU_BSWAP_NUMFUNCS);
        dsl_crypto_key_t *dck = NULL;
        uint8_t mac[ZIO_DATA_MAC_LEN] = { 0 };
-       boolean_t no_crypt;
+       boolean_t no_crypt = B_FALSE;
 
        ASSERT((HDR_GET_COMPRESS(hdr) != ZIO_COMPRESS_OFF &&
            !HDR_COMPRESSION_ENABLED(hdr)) ||
@@ -8333,6 +8338,15 @@ l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, uint64_t asize,
         * and copy the data. This may be done to elimiate a depedency on a
         * shared buffer or to reallocate the buffer to match asize.
         */
+       if (HDR_HAS_RABD(hdr) && asize != psize) {
+               ASSERT3U(size, ==, psize);
+               to_write = abd_alloc_for_io(asize, ismd);
+               abd_copy(to_write, hdr->b_crypt_hdr.b_rabd, size);
+               if (size != asize)
+                       abd_zero_off(to_write, size, asize - size);
+               goto out;
+       }
+
        if ((compress == ZIO_COMPRESS_OFF || HDR_COMPRESSION_ENABLED(hdr)) &&
            !HDR_ENCRYPTED(hdr)) {
                ASSERT3U(size, ==, psize);
@@ -8377,11 +8391,8 @@ l2arc_apply_transforms(spa_t *spa, arc_buf_hdr_t *hdr, uint64_t asize,
                if (ret != 0)
                        goto error;
 
-               if (no_crypt) {
-                       spa_keystore_dsl_key_rele(spa, dck, FTAG);
-                       abd_free(eabd);
-                       goto out;
-               }
+               if (no_crypt)
+                       abd_copy(eabd, to_write, psize);
 
                if (psize != asize)
                        abd_zero_off(eabd, psize, asize - psize);
index 1ea4c757e8c8020ed5380b25dbe091258b8dcc77..537f22011cd7817bcf0dc56d9e668de14959b6cb 100644 (file)
@@ -1175,7 +1175,7 @@ dbuf_fix_old_data(dmu_buf_impl_t *db, uint64_t txg)
         * or (if there a no active holders)
         *      just null out the current db_data pointer.
         */
-       ASSERT(dr->dr_txg >= txg - 2);
+       ASSERT3U(dr->dr_txg, >=, txg - 2);
        if (db->db_blkid == DMU_BONUS_BLKID) {
                dnode_t *dn = DB_DNODE(db);
                int bonuslen = DN_SLOTS_TO_BONUSLEN(dn->dn_num_slots);
@@ -3458,7 +3458,6 @@ dbuf_sync_leaf(dbuf_dirty_record_t *dr, dmu_tx_t *tx)
                            dn->dn_type, psize, lsize, compress_type);
                } else if (compress_type != ZIO_COMPRESS_OFF) {
                        ASSERT3U(type, ==, ARC_BUFC_DATA);
-                       int lsize = arc_buf_lsize(*datap);
                        *datap = arc_alloc_compressed_buf(os->os_spa, db,
                            psize, lsize, compress_type);
                } else {
index 4445121a0664e97fac312914d42079b6a71f1c3d..609e43fe84bcd9a9d8cac502fcfb2622c39059bd 100644 (file)
@@ -706,7 +706,9 @@ dmu_objset_own(const char *name, dmu_objset_type_t type,
 
        dsl_pool_rele(dp, FTAG);
 
-       if (dmu_objset_userobjspace_upgradable(*osp))
+       /* user accounting requires the dataset to be decrypted */
+       if (dmu_objset_userobjspace_upgradable(*osp) &&
+           (ds->ds_dir->dd_crypto_obj == 0 || decrypt))
                dmu_objset_userobjspace_upgrade(*osp);
 
        return (0);
@@ -932,7 +934,7 @@ dmu_objset_create_impl_dnstats(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
 
        if (blksz == 0)
                blksz = DNODE_BLOCK_SIZE;
-       if (blksz == 0)
+       if (ibs == 0)
                ibs = DN_MAX_INDBLKSHIFT;
 
        if (ds != NULL)
@@ -1096,7 +1098,7 @@ dmu_objset_create_sync(void *arg, dmu_tx_t *tx)
        }
 
        /*
-        * The doca_userfunc() will write out some data that needs to be
+        * The doca_userfunc() may write out some data that needs to be
         * encrypted if the dataset is encrypted (specifically the root
         * directory).  This data must be written out before the encryption
         * key mapping is removed by dsl_dataset_rele_flags().  Force the
@@ -1107,10 +1109,14 @@ dmu_objset_create_sync(void *arg, dmu_tx_t *tx)
                dsl_dataset_t *tmpds = NULL;
                boolean_t need_sync_done = B_FALSE;
 
+               mutex_enter(&ds->ds_lock);
+               ds->ds_owner = FTAG;
+               mutex_exit(&ds->ds_lock);
+
                rzio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
-               tmpds = txg_list_remove(&dp->dp_dirty_datasets, tx->tx_txg);
+               tmpds = txg_list_remove_this(&dp->dp_dirty_datasets, ds,
+                   tx->tx_txg);
                if (tmpds != NULL) {
-                       ASSERT3P(ds, ==, tmpds);
                        dsl_dataset_sync(ds, rzio, tx);
                        need_sync_done = B_TRUE;
                }
@@ -1120,9 +1126,9 @@ dmu_objset_create_sync(void *arg, dmu_tx_t *tx)
                taskq_wait(dp->dp_sync_taskq);
 
                rzio = zio_root(dp->dp_spa, NULL, NULL, ZIO_FLAG_MUSTSUCCEED);
-               tmpds = txg_list_remove(&dp->dp_dirty_datasets, tx->tx_txg);
+               tmpds = txg_list_remove_this(&dp->dp_dirty_datasets, ds,
+                   tx->tx_txg);
                if (tmpds != NULL) {
-                       ASSERT3P(ds, ==, tmpds);
                        dmu_buf_rele(ds->ds_dbuf, ds);
                        dsl_dataset_sync(ds, rzio, tx);
                }
@@ -1130,6 +1136,10 @@ dmu_objset_create_sync(void *arg, dmu_tx_t *tx)
 
                if (need_sync_done)
                        dsl_dataset_sync_done(ds, tx);
+
+               mutex_enter(&ds->ds_lock);
+               ds->ds_owner = NULL;
+               mutex_exit(&ds->ds_lock);
        }
 
        spa_history_log_internal_ds(ds, "create", tx, "");
@@ -1336,6 +1346,7 @@ dmu_objset_upgrade_stop(objset_t *os)
                mutex_exit(&os->os_upgrade_lock);
 
                taskq_cancel_id(os->os_spa->spa_upgrade_taskq, id);
+               txg_wait_synced(os->os_spa->spa_dsl_pool, 0);
        } else {
                mutex_exit(&os->os_upgrade_lock);
        }
index c63ab43e1a02c75052ceb9e2a2adcf944b084023..235e832d74801af5019a96a27a1191d5f5caea71 100644 (file)
@@ -517,7 +517,7 @@ dump_dnode(dmu_sendarg_t *dsp, const blkptr_t *bp, uint64_t object,
     dnode_phys_t *dnp)
 {
        struct drr_object *drro = &(dsp->dsa_drr->drr_u.drr_object);
-       int bonuslen = P2ROUNDUP(dnp->dn_bonuslen, 8);
+       int bonuslen;
 
        if (object < dsp->dsa_resume_object) {
                /*
@@ -558,6 +558,8 @@ dump_dnode(dmu_sendarg_t *dsp, const blkptr_t *bp, uint64_t object,
            drro->drr_blksz > SPA_OLD_MAXBLOCKSIZE)
                drro->drr_blksz = SPA_OLD_MAXBLOCKSIZE;
 
+       bonuslen = P2ROUNDUP(dnp->dn_bonuslen, 8);
+
        if ((dsp->dsa_featureflags & DMU_BACKUP_FEATURE_RAW)) {
                ASSERT(BP_IS_ENCRYPTED(bp));
 
@@ -571,7 +573,7 @@ dump_dnode(dmu_sendarg_t *dsp, const blkptr_t *bp, uint64_t object,
 
                /*
                 * Since we encrypt the entire bonus area, the (raw) part
-                * beyond the the bonuslen is actually nonzero, so we need
+                * beyond the bonuslen is actually nonzero, so we need
                 * to send it.
                 */
                if (bonuslen != 0) {
index af46dd75375cf12ad3bdf81ec5c43fc588d13d52..3c2babfda877738900a8d21fb0bf6385ea6b9ec3 100644 (file)
@@ -90,9 +90,9 @@ dsl_wrapping_key_free(dsl_wrapping_key_t *wkey)
 
        if (wkey->wk_key.ck_data) {
                bzero(wkey->wk_key.ck_data,
-                   BITS_TO_BYTES(wkey->wk_key.ck_length));
+                   CRYPTO_BITS2BYTES(wkey->wk_key.ck_length));
                kmem_free(wkey->wk_key.ck_data,
-                   BITS_TO_BYTES(wkey->wk_key.ck_length));
+                   CRYPTO_BITS2BYTES(wkey->wk_key.ck_length));
        }
 
        refcount_destroy(&wkey->wk_refcnt);
@@ -119,7 +119,7 @@ dsl_wrapping_key_create(uint8_t *wkeydata, zfs_keyformat_t keyformat,
        }
 
        wkey->wk_key.ck_format = CRYPTO_KEY_RAW;
-       wkey->wk_key.ck_length = BYTES_TO_BITS(WRAPPING_KEY_LEN);
+       wkey->wk_key.ck_length = CRYPTO_BYTES2BITS(WRAPPING_KEY_LEN);
        bcopy(wkeydata, wkey->wk_key.ck_data, WRAPPING_KEY_LEN);
 
        /* initialize the rest of the struct */
@@ -433,7 +433,6 @@ dsl_crypto_can_set_keylocation(const char *dsname, const char *keylocation)
        int ret = 0;
        dsl_dir_t *dd = NULL;
        dsl_pool_t *dp = NULL;
-       dsl_wrapping_key_t *wkey = NULL;
        uint64_t rddobj;
 
        /* hold the dsl dir */
@@ -472,16 +471,12 @@ dsl_crypto_can_set_keylocation(const char *dsname, const char *keylocation)
                goto out;
        }
 
-       if (wkey != NULL)
-               dsl_wrapping_key_rele(wkey, FTAG);
        dsl_dir_rele(dd, FTAG);
        dsl_pool_rele(dp, FTAG);
 
        return (0);
 
 out:
-       if (wkey != NULL)
-               dsl_wrapping_key_rele(wkey, FTAG);
        if (dd != NULL)
                dsl_dir_rele(dd, FTAG);
        if (dp != NULL)
@@ -1831,6 +1826,8 @@ dsl_dataset_create_crypt_sync(uint64_t dsobj, dsl_dir_t *dd,
                wkey->wk_ddobj = dd->dd_object;
        }
 
+       ASSERT3P(wkey, !=, NULL);
+
        /* Create or clone the DSL crypto key and activate the feature */
        dd->dd_crypto_obj = dsl_crypto_key_create_sync(crypt, wkey, tx);
        VERIFY0(zap_add(dp->dp_meta_objset, dd->dd_object,
@@ -2488,7 +2485,8 @@ spa_do_crypt_mac_abd(boolean_t generate, spa_t *spa, uint64_t dsobj, abd_t *abd,
                goto error;
 
        /* perform the hmac */
-       ret = zio_crypt_do_hmac(&dck->dck_key, buf, datalen, digestbuf);
+       ret = zio_crypt_do_hmac(&dck->dck_key, buf, datalen,
+           digestbuf, ZIO_DATA_MAC_LEN);
        if (ret != 0)
                goto error;
 
@@ -2604,8 +2602,7 @@ error:
                abd_return_buf(cabd, cipherbuf, datalen);
        }
 
-       if (dck != NULL)
-               spa_keystore_dsl_key_rele(spa, dck, FTAG);
+       spa_keystore_dsl_key_rele(spa, dck, FTAG);
 
        return (ret);
 }
diff --git a/module/zfs/hkdf.c b/module/zfs/hkdf.c
new file mode 100644 (file)
index 0000000..1426547
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2017, Datto, Inc. All rights reserved.
+ */
+
+#include <sys/crypto/api.h>
+#include <sys/sha2.h>
+#include <sys/hkdf.h>
+
+static int
+hkdf_sha512_extract(uint8_t *salt, uint_t salt_len, uint8_t *key_material,
+    uint_t km_len, uint8_t *out_buf)
+{
+       int ret;
+       crypto_mechanism_t mech;
+       crypto_key_t key;
+       crypto_data_t input_cd, output_cd;
+
+       /* initialize HMAC mechanism */
+       mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC);
+       mech.cm_param = NULL;
+       mech.cm_param_len = 0;
+
+       /* initialize the salt as a crypto key */
+       key.ck_format = CRYPTO_KEY_RAW;
+       key.ck_length = CRYPTO_BYTES2BITS(salt_len);
+       key.ck_data = salt;
+
+       /* initialize crypto data for the input and output data */
+       input_cd.cd_format = CRYPTO_DATA_RAW;
+       input_cd.cd_offset = 0;
+       input_cd.cd_length = km_len;
+       input_cd.cd_raw.iov_base = (char *)key_material;
+       input_cd.cd_raw.iov_len = input_cd.cd_length;
+
+       output_cd.cd_format = CRYPTO_DATA_RAW;
+       output_cd.cd_offset = 0;
+       output_cd.cd_length = SHA512_DIGEST_LENGTH;
+       output_cd.cd_raw.iov_base = (char *)out_buf;
+       output_cd.cd_raw.iov_len = output_cd.cd_length;
+
+       ret = crypto_mac(&mech, &input_cd, &key, NULL, &output_cd, NULL);
+       if (ret != CRYPTO_SUCCESS)
+               return (SET_ERROR(EIO));
+
+       return (0);
+}
+
+static int
+hkdf_sha512_expand(uint8_t *extract_key, uint8_t *info, uint_t info_len,
+    uint8_t *out_buf, uint_t out_len)
+{
+       int ret;
+       crypto_mechanism_t mech;
+       crypto_context_t ctx;
+       crypto_key_t key;
+       crypto_data_t T_cd, info_cd, c_cd;
+       uint_t i, T_len = 0, pos = 0;
+       uint8_t c;
+       uint_t N = (out_len + SHA512_DIGEST_LENGTH) / SHA512_DIGEST_LENGTH;
+       uint8_t T[SHA512_DIGEST_LENGTH];
+
+       if (N > 255)
+               return (SET_ERROR(EINVAL));
+
+       /* initialize HMAC mechanism */
+       mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC);
+       mech.cm_param = NULL;
+       mech.cm_param_len = 0;
+
+       /* initialize the salt as a crypto key */
+       key.ck_format = CRYPTO_KEY_RAW;
+       key.ck_length = CRYPTO_BYTES2BITS(SHA512_DIGEST_LENGTH);
+       key.ck_data = extract_key;
+
+       /* initialize crypto data for the input and output data */
+       T_cd.cd_format = CRYPTO_DATA_RAW;
+       T_cd.cd_offset = 0;
+       T_cd.cd_raw.iov_base = (char *)T;
+
+       c_cd.cd_format = CRYPTO_DATA_RAW;
+       c_cd.cd_offset = 0;
+       c_cd.cd_length = 1;
+       c_cd.cd_raw.iov_base = (char *)&c;
+       c_cd.cd_raw.iov_len = c_cd.cd_length;
+
+       info_cd.cd_format = CRYPTO_DATA_RAW;
+       info_cd.cd_offset = 0;
+       info_cd.cd_length = info_len;
+       info_cd.cd_raw.iov_base = (char *)info;
+       info_cd.cd_raw.iov_len = info_cd.cd_length;
+
+       for (i = 1; i <= N; i++) {
+               c = i;
+
+               T_cd.cd_length = T_len;
+               T_cd.cd_raw.iov_len = T_cd.cd_length;
+
+               ret = crypto_mac_init(&mech, &key, NULL, &ctx, NULL);
+               if (ret != CRYPTO_SUCCESS)
+                       return (SET_ERROR(EIO));
+
+               ret = crypto_mac_update(ctx, &T_cd, NULL);
+               if (ret != CRYPTO_SUCCESS)
+                       return (SET_ERROR(EIO));
+
+               ret = crypto_mac_update(ctx, &info_cd, NULL);
+               if (ret != CRYPTO_SUCCESS)
+                       return (SET_ERROR(EIO));
+
+               ret = crypto_mac_update(ctx, &c_cd, NULL);
+               if (ret != CRYPTO_SUCCESS)
+                       return (SET_ERROR(EIO));
+
+               T_len = SHA512_DIGEST_LENGTH;
+               T_cd.cd_length = T_len;
+               T_cd.cd_raw.iov_len = T_cd.cd_length;
+
+               ret = crypto_mac_final(ctx, &T_cd, NULL);
+               if (ret != CRYPTO_SUCCESS)
+                       return (SET_ERROR(EIO));
+
+               bcopy(T, out_buf + pos,
+                   (i != N) ? SHA512_DIGEST_LENGTH : (out_len - pos));
+               pos += SHA512_DIGEST_LENGTH;
+       }
+
+       return (0);
+}
+
+/*
+ * HKDF is designed to be a relatively fast function for deriving keys from a
+ * master key + a salt. We use this function to generate new encryption keys
+ * so as to avoid hitting the cryptographic limits of the underlying
+ * encryption modes. Note that, for the sake of deriving encryption keys, the
+ * info parameter is called the "salt" everywhere else in the code.
+ */
+int
+hkdf_sha512(uint8_t *key_material, uint_t km_len, uint8_t *salt,
+    uint_t salt_len, uint8_t *info, uint_t info_len, uint8_t *output_key,
+    uint_t out_len)
+{
+       int ret;
+       uint8_t extract_key[SHA512_DIGEST_LENGTH];
+
+       ret = hkdf_sha512_extract(salt, salt_len, key_material, km_len,
+           extract_key);
+       if (ret != 0)
+               return (ret);
+
+       ret = hkdf_sha512_expand(extract_key, info, info_len, output_key,
+           out_len);
+       if (ret != 0)
+               return (ret);
+
+       return (0);
+}
index f15e8cddbe9ec6999637720ba4d16e024ab45ad9..e90e583ae06eef8735d44b7a726aa344600f2ead 100644 (file)
@@ -2115,6 +2115,21 @@ zil_suspend(const char *osname, void **cookiep)
                return (0);
        }
 
+       /*
+        * The ZIL has work to do. Ensure that the associated encryption
+        * key will remain mapped while we are committing the log by
+        * grabbing a reference to it. If the key isn't loaded we have no
+        * choice but to return an error until the wrapping key is loaded.
+        */
+       if (os->os_encrypted && spa_keystore_create_mapping(os->os_spa,
+           dmu_objset_ds(os), FTAG) != 0) {
+               zilog->zl_suspend--;
+               mutex_exit(&zilog->zl_lock);
+               dsl_dataset_long_rele(dmu_objset_ds(os), suspend_tag);
+               dsl_dataset_rele(dmu_objset_ds(os), suspend_tag);
+               return (SET_ERROR(EBUSY));
+       }
+
        zilog->zl_suspending = B_TRUE;
        mutex_exit(&zilog->zl_lock);
 
@@ -2127,6 +2142,20 @@ zil_suspend(const char *osname, void **cookiep)
        cv_broadcast(&zilog->zl_cv_suspend);
        mutex_exit(&zilog->zl_lock);
 
+       if (os->os_encrypted) {
+               /*
+                * Encrypted datasets need to wait for all data to be
+                * synced out before removing the mapping.
+                *
+                * XXX: Depending on the number of datasets with
+                * outstanding ZIL data on a given log device, this
+                * might cause spa_offline_log() to take a long time.
+                */
+               txg_wait_synced(zilog->zl_dmu_pool, zilog->zl_destroy_txg);
+               VERIFY0(spa_keystore_remove_mapping(os->os_spa,
+                   dmu_objset_id(os), FTAG));
+       }
+
        if (cookiep == NULL)
                zil_resume(os);
        else
index 057a1405f5f56786839f15909d005736a846c876..508011a86bf7c8715e6e4e623a03c0e7e4e90f66 100644 (file)
@@ -2518,13 +2518,14 @@ zio_write_gang_block(zio_t *pio)
 
                zp.zp_checksum = gio->io_prop.zp_checksum;
                zp.zp_compress = ZIO_COMPRESS_OFF;
-               zp.zp_encrypt = gio->io_prop.zp_encrypt;
                zp.zp_type = DMU_OT_NONE;
                zp.zp_level = 0;
                zp.zp_copies = gio->io_prop.zp_copies;
                zp.zp_dedup = B_FALSE;
                zp.zp_dedup_verify = B_FALSE;
                zp.zp_nopwrite = B_FALSE;
+               zp.zp_encrypt = gio->io_prop.zp_encrypt;
+               zp.zp_byteorder = gio->io_prop.zp_byteorder;
                bzero(zp.zp_salt, ZIO_DATA_SALT_LEN);
                bzero(zp.zp_iv, ZIO_DATA_IV_LEN);
                bzero(zp.zp_mac, ZIO_DATA_MAC_LEN);
index 8fcf5155005387bba6a305ec43c1630a8c619224..6238e6f74ac6cde2bf220865d4d63bde0d7a68a0 100644 (file)
@@ -25,6 +25,7 @@
 #include <sys/zio.h>
 #include <sys/zil.h>
 #include <sys/sha2.h>
+#include <sys/hkdf.h>
 
 /*
  * This file is responsible for handling all of the details of generating
@@ -198,176 +199,6 @@ zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS] = {
        {SUN_CKM_AES_GCM,       ZC_TYPE_GCM,    32,     "aes-256-gcm"}
 };
 
-static int
-hkdf_sha512_extract(uint8_t *salt, uint_t salt_len, uint8_t *key_material,
-    uint_t km_len, uint8_t *out_buf)
-{
-       int ret;
-       crypto_mechanism_t mech;
-       crypto_key_t key;
-       crypto_data_t input_cd, output_cd;
-
-       /* initialize HMAC mechanism */
-       mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC);
-       mech.cm_param = NULL;
-       mech.cm_param_len = 0;
-
-       /* initialize the salt as a crypto key */
-       key.ck_format = CRYPTO_KEY_RAW;
-       key.ck_length = BYTES_TO_BITS(salt_len);
-       key.ck_data = salt;
-
-       /* initialize crypto data for the input and output data */
-       input_cd.cd_format = CRYPTO_DATA_RAW;
-       input_cd.cd_offset = 0;
-       input_cd.cd_length = km_len;
-       input_cd.cd_raw.iov_base = (char *)key_material;
-       input_cd.cd_raw.iov_len = input_cd.cd_length;
-
-       output_cd.cd_format = CRYPTO_DATA_RAW;
-       output_cd.cd_offset = 0;
-       output_cd.cd_length = SHA512_DIGEST_LEN;
-       output_cd.cd_raw.iov_base = (char *)out_buf;
-       output_cd.cd_raw.iov_len = output_cd.cd_length;
-
-       ret = crypto_mac(&mech, &input_cd, &key, NULL, &output_cd, NULL);
-       if (ret != CRYPTO_SUCCESS) {
-               ret = SET_ERROR(EIO);
-               goto error;
-       }
-
-       return (0);
-
-error:
-       return (ret);
-}
-
-static int
-hkdf_sha512_expand(uint8_t *extract_key, uint8_t *info, uint_t info_len,
-    uint8_t *out_buf, uint_t out_len)
-{
-       int ret;
-       crypto_mechanism_t mech;
-       crypto_context_t ctx;
-       crypto_key_t key;
-       crypto_data_t T_cd, info_cd, c_cd;
-       uint_t i, T_len = 0, pos = 0;
-       uint8_t c;
-       uint_t N = (out_len + SHA512_DIGEST_LEN) / SHA512_DIGEST_LEN;
-       uint8_t T[SHA512_DIGEST_LEN];
-
-       if (N > 255)
-               return (SET_ERROR(EINVAL));
-
-       /* initialize HMAC mechanism */
-       mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC);
-       mech.cm_param = NULL;
-       mech.cm_param_len = 0;
-
-       /* initialize the salt as a crypto key */
-       key.ck_format = CRYPTO_KEY_RAW;
-       key.ck_length = BYTES_TO_BITS(SHA512_DIGEST_LEN);
-       key.ck_data = extract_key;
-
-       /* initialize crypto data for the input and output data */
-       T_cd.cd_format = CRYPTO_DATA_RAW;
-       T_cd.cd_offset = 0;
-       T_cd.cd_raw.iov_base = (char *)T;
-
-       c_cd.cd_format = CRYPTO_DATA_RAW;
-       c_cd.cd_offset = 0;
-       c_cd.cd_length = 1;
-       c_cd.cd_raw.iov_base = (char *)&c;
-       c_cd.cd_raw.iov_len = c_cd.cd_length;
-
-       info_cd.cd_format = CRYPTO_DATA_RAW;
-       info_cd.cd_offset = 0;
-       info_cd.cd_length = info_len;
-       info_cd.cd_raw.iov_base = (char *)info;
-       info_cd.cd_raw.iov_len = info_cd.cd_length;
-
-       for (i = 1; i <= N; i++) {
-               c = i;
-
-               T_cd.cd_length = T_len;
-               T_cd.cd_raw.iov_len = T_cd.cd_length;
-
-               ret = crypto_mac_init(&mech, &key, NULL, &ctx, NULL);
-               if (ret != CRYPTO_SUCCESS) {
-                       ret = SET_ERROR(EIO);
-                       goto error;
-               }
-
-               ret = crypto_mac_update(ctx, &T_cd, NULL);
-               if (ret != CRYPTO_SUCCESS) {
-                       ret = SET_ERROR(EIO);
-                       goto error;
-               }
-
-               ret = crypto_mac_update(ctx, &info_cd, NULL);
-               if (ret != CRYPTO_SUCCESS) {
-                       ret = SET_ERROR(EIO);
-                       goto error;
-               }
-
-               ret = crypto_mac_update(ctx, &c_cd, NULL);
-               if (ret != CRYPTO_SUCCESS) {
-                       ret = SET_ERROR(EIO);
-                       goto error;
-               }
-
-               T_len = SHA512_DIGEST_LEN;
-               T_cd.cd_length = T_len;
-               T_cd.cd_raw.iov_len = T_cd.cd_length;
-
-               ret = crypto_mac_final(ctx, &T_cd, NULL);
-               if (ret != CRYPTO_SUCCESS) {
-                       ret = SET_ERROR(EIO);
-                       goto error;
-               }
-
-               bcopy(T, out_buf + pos,
-                   (i != N) ? SHA512_DIGEST_LEN : (out_len - pos));
-               pos += SHA512_DIGEST_LEN;
-       }
-
-       return (0);
-
-error:
-       return (ret);
-}
-
-/*
- * HKDF is designed to be a relatively fast function for deriving keys from a
- * master key + a salt. We use this function to generate new encryption keys
- * so as to avoid hitting the cryptographic limits of the underlying
- * encryption modes. Note that, for the sake of deriving encryption keys, the
- * info parameter is called the "salt" everywhere else in the code.
- */
-static int
-hkdf_sha512(uint8_t *key_material, uint_t km_len, uint8_t *salt,
-    uint_t salt_len, uint8_t *info, uint_t info_len, uint8_t *output_key,
-    uint_t out_len)
-{
-       int ret;
-       uint8_t extract_key[SHA512_DIGEST_LEN];
-
-       ret = hkdf_sha512_extract(salt, salt_len, key_material, km_len,
-           extract_key);
-       if (ret != 0)
-               goto error;
-
-       ret = hkdf_sha512_expand(extract_key, info, info_len, output_key,
-           out_len);
-       if (ret != 0)
-               goto error;
-
-       return (0);
-
-error:
-       return (ret);
-}
-
 void
 zio_crypt_key_destroy(zio_crypt_key_t *key)
 {
@@ -421,11 +252,11 @@ zio_crypt_key_init(uint64_t crypt, zio_crypt_key_t *key)
        /* initialize keys for the ICP */
        key->zk_current_key.ck_format = CRYPTO_KEY_RAW;
        key->zk_current_key.ck_data = key->zk_current_keydata;
-       key->zk_current_key.ck_length = BYTES_TO_BITS(keydata_len);
+       key->zk_current_key.ck_length = CRYPTO_BYTES2BITS(keydata_len);
 
        key->zk_hmac_key.ck_format = CRYPTO_KEY_RAW;
        key->zk_hmac_key.ck_data = &key->zk_hmac_key;
-       key->zk_hmac_key.ck_length = BYTES_TO_BITS(SHA512_HMAC_KEYLEN);
+       key->zk_hmac_key.ck_length = CRYPTO_BYTES2BITS(SHA512_HMAC_KEYLEN);
 
        /*
         * Initialize the crypto templates. It's ok if this fails because
@@ -588,10 +419,10 @@ zio_do_crypt_uio(boolean_t encrypt, uint64_t crypt, crypto_key_t *key,
                mech.cm_param_len = sizeof (CK_AES_CCM_PARAMS);
        } else {
                gcmp.ulIvLen = ZIO_DATA_IV_LEN;
-               gcmp.ulIvBits = BYTES_TO_BITS(ZIO_DATA_IV_LEN);
+               gcmp.ulIvBits = CRYPTO_BYTES2BITS(ZIO_DATA_IV_LEN);
                gcmp.ulAADLen = auth_len;
                gcmp.pAAD = authbuf;
-               gcmp.ulTagBits = BYTES_TO_BITS(maclen);
+               gcmp.ulTagBits = CRYPTO_BYTES2BITS(maclen);
                gcmp.pIv = ivbuf;
 
                mech.cm_param = (char *)(&gcmp);
@@ -748,11 +579,11 @@ zio_crypt_key_unwrap(crypto_key_t *cwkey, uint64_t crypt, uint64_t guid,
        /* initialize keys for ICP */
        key->zk_current_key.ck_format = CRYPTO_KEY_RAW;
        key->zk_current_key.ck_data = key->zk_current_keydata;
-       key->zk_current_key.ck_length = BYTES_TO_BITS(keydata_len);
+       key->zk_current_key.ck_length = CRYPTO_BYTES2BITS(keydata_len);
 
        key->zk_hmac_key.ck_format = CRYPTO_KEY_RAW;
        key->zk_hmac_key.ck_data = key->zk_hmac_keydata;
-       key->zk_hmac_key.ck_length = BYTES_TO_BITS(SHA512_HMAC_KEYLEN);
+       key->zk_hmac_key.ck_length = CRYPTO_BYTES2BITS(SHA512_HMAC_KEYLEN);
 
        /*
         * Initialize the crypto templates. It's ok if this fails because
@@ -801,12 +632,14 @@ error:
 
 int
 zio_crypt_do_hmac(zio_crypt_key_t *key, uint8_t *data, uint_t datalen,
-    uint8_t *digestbuf)
+    uint8_t *digestbuf, uint_t digestlen)
 {
        int ret;
        crypto_mechanism_t mech;
        crypto_data_t in_data, digest_data;
-       uint8_t raw_digestbuf[SHA512_DIGEST_LEN];
+       uint8_t raw_digestbuf[SHA512_DIGEST_LENGTH];
+
+       ASSERT3U(digestlen, <=, SHA512_DIGEST_LENGTH);
 
        /* initialize sha512-hmac mechanism and crypto data */
        mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC);
@@ -822,7 +655,7 @@ zio_crypt_do_hmac(zio_crypt_key_t *key, uint8_t *data, uint_t datalen,
 
        digest_data.cd_format = CRYPTO_DATA_RAW;
        digest_data.cd_offset = 0;
-       digest_data.cd_length = SHA512_DIGEST_LEN;
+       digest_data.cd_length = SHA512_DIGEST_LENGTH;
        digest_data.cd_raw.iov_base = (char *)raw_digestbuf;
        digest_data.cd_raw.iov_len = digest_data.cd_length;
 
@@ -834,12 +667,12 @@ zio_crypt_do_hmac(zio_crypt_key_t *key, uint8_t *data, uint_t datalen,
                goto error;
        }
 
-       bcopy(raw_digestbuf, digestbuf, ZIO_DATA_MAC_LEN);
+       bcopy(raw_digestbuf, digestbuf, digestlen);
 
        return (0);
 
 error:
-       bzero(digestbuf, ZIO_DATA_MAC_LEN);
+       bzero(digestbuf, digestlen);
        return (ret);
 }
 
@@ -848,9 +681,10 @@ zio_crypt_generate_iv_salt_dedup(zio_crypt_key_t *key, uint8_t *data,
     uint_t datalen, uint8_t *ivbuf, uint8_t *salt)
 {
        int ret;
-       uint8_t digestbuf[SHA512_DIGEST_LEN];
+       uint8_t digestbuf[SHA512_DIGEST_LENGTH];
 
-       ret = zio_crypt_do_hmac(key, data, datalen, digestbuf);
+       ret = zio_crypt_do_hmac(key, data, datalen,
+           digestbuf, SHA512_DIGEST_LENGTH);
        if (ret != 0)
                return (ret);
 
@@ -1212,8 +1046,8 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen,
        objset_phys_t *osp = data;
        uint64_t intval;
        boolean_t le_bswap = (should_bswap == ZFS_HOST_BYTEORDER);
-       uint8_t raw_portable_mac[SHA512_DIGEST_LEN];
-       uint8_t raw_local_mac[SHA512_DIGEST_LEN];
+       uint8_t raw_portable_mac[SHA512_DIGEST_LENGTH];
+       uint8_t raw_local_mac[SHA512_DIGEST_LENGTH];
 
        /* initialize HMAC mechanism */
        mech.cm_type = crypto_mech2id(SUN_CKM_SHA512_HMAC);
@@ -1267,7 +1101,7 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen,
                goto error;
 
        /* store the final digest in a temporary buffer and copy what we need */
-       cd.cd_length = SHA512_DIGEST_LEN;
+       cd.cd_length = SHA512_DIGEST_LENGTH;
        cd.cd_raw.iov_base = (char *)raw_portable_mac;
        cd.cd_raw.iov_len = cd.cd_length;
 
@@ -1284,7 +1118,7 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen,
         * objects are not present, the local MAC is zeroed out.
         */
        if (osp->os_userused_dnode.dn_type == DMU_OT_NONE &&
-           osp->os_userused_dnode.dn_type == DMU_OT_NONE) {
+           osp->os_groupused_dnode.dn_type == DMU_OT_NONE) {
                bzero(local_mac, ZIO_OBJSET_MAC_LEN);
                return (0);
        }
@@ -1326,7 +1160,7 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen,
                goto error;
 
        /* store the final digest in a temporary buffer and copy what we need */
-       cd.cd_length = SHA512_DIGEST_LEN;
+       cd.cd_length = SHA512_DIGEST_LENGTH;
        cd.cd_raw.iov_base = (char *)raw_local_mac;
        cd.cd_raw.iov_len = cd.cd_length;
 
@@ -1367,7 +1201,7 @@ zio_crypt_do_indirect_mac_checksum(boolean_t generate, void *buf,
        blkptr_t *bp;
        int i, epb = datalen >> SPA_BLKPTRSHIFT;
        SHA2_CTX ctx;
-       uint8_t digestbuf[SHA512_DIGEST_LEN];
+       uint8_t digestbuf[SHA512_DIGEST_LENGTH];
 
        /* checksum all of the MACs from the layer below */
        SHA2Init(SHA512, &ctx);
@@ -1468,7 +1302,7 @@ zio_crypt_init_uios_zil(boolean_t encrypt, uint8_t *plainbuf,
        /* allocate the iovec arrays */
        if (nr_src != 0) {
                src_iovecs = kmem_alloc(nr_src * sizeof (iovec_t), KM_SLEEP);
-               if (!src_iovecs) {
+               if (src_iovecs == NULL) {
                        ret = SET_ERROR(ENOMEM);
                        goto error;
                }
@@ -1476,7 +1310,7 @@ zio_crypt_init_uios_zil(boolean_t encrypt, uint8_t *plainbuf,
 
        if (nr_dst != 0) {
                dst_iovecs = kmem_alloc(nr_dst * sizeof (iovec_t), KM_SLEEP);
-               if (!dst_iovecs) {
+               if (dst_iovecs == NULL) {
                        ret = SET_ERROR(ENOMEM);
                        goto error;
                }
@@ -1515,6 +1349,9 @@ zio_crypt_init_uios_zil(boolean_t encrypt, uint8_t *plainbuf,
                aadp += sizeof (lr_t);
                aad_len += sizeof (lr_t);
 
+               ASSERT3P(src_iovecs, !=, NULL);
+               ASSERT3P(dst_iovecs, !=, NULL);
+
                /*
                 * If this is a TX_WRITE record we want to encrypt everything
                 * except the bp if exists. If the bp does exist we want to
@@ -1655,7 +1492,7 @@ zio_crypt_init_uios_dnode(boolean_t encrypt, uint8_t *plainbuf,
 
        if (nr_src != 0) {
                src_iovecs = kmem_alloc(nr_src * sizeof (iovec_t), KM_SLEEP);
-               if (!src_iovecs) {
+               if (src_iovecs == NULL) {
                        ret = SET_ERROR(ENOMEM);
                        goto error;
                }
@@ -1663,7 +1500,7 @@ zio_crypt_init_uios_dnode(boolean_t encrypt, uint8_t *plainbuf,
 
        if (nr_dst != 0) {
                dst_iovecs = kmem_alloc(nr_dst * sizeof (iovec_t), KM_SLEEP);
-               if (!dst_iovecs) {
+               if (dst_iovecs == NULL) {
                        ret = SET_ERROR(ENOMEM);
                        goto error;
                }
@@ -1729,6 +1566,10 @@ zio_crypt_init_uios_dnode(boolean_t encrypt, uint8_t *plainbuf,
                if (dnp->dn_type != DMU_OT_NONE &&
                    DMU_OT_IS_ENCRYPTED(dnp->dn_bonustype) &&
                    dnp->dn_bonuslen != 0) {
+                       ASSERT3U(nr_iovecs, <, nr_src);
+                       ASSERT3U(nr_iovecs, <, nr_dst);
+                       ASSERT3P(src_iovecs, !=, NULL);
+                       ASSERT3P(dst_iovecs, !=, NULL);
                        src_iovecs[nr_iovecs].iov_base = DN_BONUS(dnp);
                        src_iovecs[nr_iovecs].iov_len = crypt_len;
                        dst_iovecs[nr_iovecs].iov_base = DN_BONUS(&ddnp[i]);
@@ -1942,7 +1783,7 @@ zio_do_crypt_data(boolean_t encrypt, zio_crypt_key_t *key, uint8_t *salt,
 
                tmp_ckey.ck_format = CRYPTO_KEY_RAW;
                tmp_ckey.ck_data = enc_keydata;
-               tmp_ckey.ck_length = BYTES_TO_BITS(keydata_len);
+               tmp_ckey.ck_length = CRYPTO_BYTES2BITS(keydata_len);
 
                ckey = &tmp_ckey;
                tmpl = NULL;
index 3f11c2a509c62acfead78bb02daced57fe564a9f..14f2929c64ffd7ab6ad875de02272defc89f24cd 100644 (file)
@@ -87,6 +87,8 @@ BuildRequires:  libuuid-devel
 BuildRequires:  libblkid-devel
 BuildRequires:  libudev-devel
 BuildRequires:  libattr-devel
+BuildRequires:  openssl-devel
+Requires:       openssl
 %endif
 %if 0%{?_systemd}
 Requires(post): systemd
@@ -183,7 +185,7 @@ Requires:   fio
 Requires:      acl
 Requires:      sudo
 Requires:      sysstat
-Requires:       rng-tools
+Requires:      rng-tools
 
 %description test
 This package contains test infrastructure and support scripts for
index f285d6a252365e12679868b45733242da7c5a84f..3139bd42ba935b63ceffa7175e4656f4c25c5b46 100755 (executable)
@@ -146,6 +146,15 @@ function store_core
        fi
 }
 
+rngdpid=""
+function on_exit
+{
+       if [ -n "$rngdpid" ]; then
+               kill -9 "$rngdpid"
+       fi
+}
+trap on_exit EXIT
+
 # parse arguments
 # expected format: zloop [-t timeout] [-c coredir] [-- extra ztest args]
 coredir=$DEFAULTCOREDIR
@@ -191,6 +200,9 @@ or_die rm -f ztest.history
 or_die rm -f ztest.ddt
 or_die rm -f ztest.cores
 
+# start rngd in the background so we don't run out of entropy
+or_die read -r rngdpid < <(rngd -f -r /dev/urandom & echo $!)
+
 ztrc=0         # ztest return value
 foundcrashes=0 # number of crashes found so far
 starttime=$(date +%s)
index d425406b761f1360187fbe0339e26be10c9af8e1..47ddc6bc3adde6fd2b2cd70038df505ed02876d6 100644 (file)
@@ -405,6 +405,9 @@ tests = ['history_001_pos', 'history_002_pos', 'history_003_pos',
     'history_007_pos', 'history_008_pos', 'history_009_pos',
     'history_010_pos']
 
+[tests/functional/hkdf]
+tests = ['run_hkdf_test']
+
 [tests/functional/inheritance]
 tests = ['inherit_001_pos']
 pre =
index d9f36dda3e6609c42701ed5f77816dcf7235a1c2..e2d0c4162e00d0c7e917918ab7404899e5cca074 100644 (file)
@@ -12,6 +12,7 @@ export SYSTEM_FILES='arp
     attr
     basename
     bc
+    blkid
     blockdev
     bunzip2
     bzcat
index d68f254ef27577c9a922c76a25b7e702f32bf32f..dc4bf51b4f733e19def15eb8af1ec771dfb103cd 100644 (file)
@@ -21,6 +21,7 @@ SUBDIRS = \
        grow_pool \
        grow_replicas \
        history \
+       hkdf \
        inheritance \
        inuse \
        large_files \
index 2e31af3c2260e1dd57185d4337f09ec718be3e9f..df16da992b1a2cd1ec9599cd2e2125f80a3a7fbe 100755 (executable)
 
 verify_runnable "global"
 
+if is_linux; then
+       # Versions of libblkid older than 2.27.0 will not always detect member
+       # devices of a pool, therefore skip this test case for old versions.
+       currentver="$(blkid -v | tr ',' ' ' | awk '/libblkid/ { print $6 }')"
+       requiredver="2.27.0"
+
+       if [ "$(printf "$requiredver\n$currentver" | sort -V | head -n1)" ==  \
+           "$currentver" ] && [ "$currentver" != "$requiredver" ]; then
+               log_unsupported "libblkid ($currentver) may not detect pools"
+       fi
+fi
+
 function cleanup
 {
        if [[ $exported_pool == true ]]; then
diff --git a/tests/zfs-tests/tests/functional/hkdf/.gitignore b/tests/zfs-tests/tests/functional/hkdf/.gitignore
new file mode 100644 (file)
index 0000000..828e33d
--- /dev/null
@@ -0,0 +1 @@
+hkdf_test
diff --git a/tests/zfs-tests/tests/functional/hkdf/Makefile.am b/tests/zfs-tests/tests/functional/hkdf/Makefile.am
new file mode 100644 (file)
index 0000000..37fe2ed
--- /dev/null
@@ -0,0 +1,21 @@
+include $(top_srcdir)/config/Rules.am
+AM_CPPFLAGS += -I$(top_srcdir)/include
+AM_CPPFLAGS += -I$(top_srcdir)/lib/libspl/include
+LDADD = $(top_srcdir)/lib/libicp/libicp.la
+LDADD += $(top_srcdir)/lib/libzpool/libzpool.la
+
+AUTOMAKE_OPTIONS = subdir-objects
+
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/hkdf
+
+dist_pkgdata_SCRIPTS = \
+       setup.ksh \
+       cleanup.ksh \
+       run_hkdf_test.ksh
+
+pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/hkdf
+
+pkgexec_PROGRAMS = \
+       hkdf_test
+
+hkdf_test_SOURCES = hkdf_test.c
diff --git a/tests/zfs-tests/tests/functional/hkdf/cleanup.ksh b/tests/zfs-tests/tests/functional/hkdf/cleanup.ksh
new file mode 100755 (executable)
index 0000000..2bdca19
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/ksh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2017 by Datto Inc. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "global"
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/hkdf/hkdf_test.c b/tests/zfs-tests/tests/functional/hkdf/hkdf_test.c
new file mode 100644 (file)
index 0000000..dd23da3
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * CDDL HEADER START
+ *
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source.  A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2017, Datto, Inc. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <sys/crypto/icp.h>
+#include <sys/sha2.h>
+#include <sys/hkdf.h>
+
+#define        NELEMS(x)  (sizeof (x) / sizeof ((x)[0]))
+
+/*
+ * Byte arrays are given as char pointers so that they
+ * can be specified as strings.
+ */
+typedef struct hkdf_tv {
+       /* test vector input values */
+       char            *ikm;
+       uint_t          ikm_len;
+       char            *salt;
+       uint_t          salt_len;
+       char            *info;
+       uint_t          info_len;
+       uint_t          okm_len;
+
+       /* expected output */
+       char            *okm;
+} hkdf_tv_t;
+
+/*
+ * XXX Neither NIST nor IETF has published official test
+ * vectors for testing HKDF with SHA512. The following
+ * vectors should be updated if these are ever published.
+ * The current vectors were taken from:
+ * https://www.kullo.net/blog/hkdf-sha-512-test-vectors/
+ */
+static hkdf_tv_t test_vectors[] = {
+       {
+               .ikm =  "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+                       "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+                       "\x0b\x0b\x0b\x0b\x0b\x0b",
+               .ikm_len = 22,
+               .salt = "\x00\x01\x02\x03\x04\x05\x06\x07"
+                       "\x08\x09\x0a\x0b\x0c",
+               .salt_len = 13,
+               .info = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+                       "\xf8\xf9",
+               .info_len = 10,
+               .okm_len = 42,
+               .okm =  "\x83\x23\x90\x08\x6c\xda\x71\xfb"
+                       "\x47\x62\x5b\xb5\xce\xb1\x68\xe4"
+                       "\xc8\xe2\x6a\x1a\x16\xed\x34\xd9"
+                       "\xfc\x7f\xe9\x2c\x14\x81\x57\x93"
+                       "\x38\xda\x36\x2c\xb8\xd9\xf9\x25"
+                       "\xd7\xcb",
+       },
+       {
+               .ikm =  "\x00\x01\x02\x03\x04\x05\x06\x07"
+                       "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+                       "\x10\x11\x12\x13\x14\x15\x16\x17"
+                       "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+                       "\x20\x21\x22\x23\x24\x25\x26\x27"
+                       "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
+                       "\x30\x31\x32\x33\x34\x35\x36\x37"
+                       "\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
+                       "\x40\x41\x42\x43\x44\x45\x46\x47"
+                       "\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f",
+               .ikm_len = 80,
+               .salt = "\x60\x61\x62\x63\x64\x65\x66\x67"
+                       "\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
+                       "\x70\x71\x72\x73\x74\x75\x76\x77"
+                       "\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
+                       "\x80\x81\x82\x83\x84\x85\x86\x87"
+                       "\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+                       "\x90\x91\x92\x93\x94\x95\x96\x97"
+                       "\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
+                       "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7"
+                       "\xa8\xa9\xaa\xab\xac\xad\xae\xaf",
+               .salt_len = 80,
+               .info = "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
+                       "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
+                       "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7"
+                       "\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+                       "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7"
+                       "\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
+                       "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7"
+                       "\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+                       "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+                       "\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+               .info_len = 80,
+               .okm_len = 42,
+               .okm =  "\xce\x6c\x97\x19\x28\x05\xb3\x46"
+                       "\xe6\x16\x1e\x82\x1e\xd1\x65\x67"
+                       "\x3b\x84\xf4\x00\xa2\xb5\x14\xb2"
+                       "\xfe\x23\xd8\x4c\xd1\x89\xdd\xf1"
+                       "\xb6\x95\xb4\x8c\xbd\x1c\x83\x88"
+                       "\x44\x11\x37\xb3\xce\x28\xf1\x6a"
+                       "\xa6\x4b\xa3\x3b\xa4\x66\xb2\x4d"
+                       "\xf6\xcf\xcb\x02\x1e\xcf\xf2\x35"
+                       "\xf6\xa2\x05\x6c\xe3\xaf\x1d\xe4"
+                       "\x4d\x57\x20\x97\xa8\x50\x5d\x9e"
+                       "\x7a\x93",
+       },
+       {
+               .ikm =  "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+                       "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+                       "\x0b\x0b\x0b\x0b\x0b\x0b",
+               .ikm_len = 22,
+               .salt = NULL,
+               .salt_len = 0,
+               .info = NULL,
+               .info_len = 0,
+               .okm_len = 42,
+               .okm =  "\xf5\xfa\x02\xb1\x82\x98\xa7\x2a"
+                       "\x8c\x23\x89\x8a\x87\x03\x47\x2c"
+                       "\x6e\xb1\x79\xdc\x20\x4c\x03\x42"
+                       "\x5c\x97\x0e\x3b\x16\x4b\xf9\x0f"
+                       "\xff\x22\xd0\x48\x36\xd0\xe2\x34"
+                       "\x3b\xac",
+       },
+       {
+               .ikm =  "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+                       "\x0b\x0b\x0b",
+               .ikm_len = 11,
+               .salt = "\x00\x01\x02\x03\x04\x05\x06\x07"
+                       "\x08\x09\x0a\x0b\x0c",
+               .salt_len = 13,
+               .info = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
+                       "\xf8\xf9",
+               .info_len = 10,
+               .okm_len = 42,
+               .okm =  "\x74\x13\xe8\x99\x7e\x02\x06\x10"
+                       "\xfb\xf6\x82\x3f\x2c\xe1\x4b\xff"
+                       "\x01\x87\x5d\xb1\xca\x55\xf6\x8c"
+                       "\xfc\xf3\x95\x4d\xc8\xaf\xf5\x35"
+                       "\x59\xbd\x5e\x30\x28\xb0\x80\xf7"
+                       "\xc0\x68",
+       },
+       {
+               .ikm =  "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+                       "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+                       "\x0c\x0c\x0c\x0c\x0c\x0c",
+               .ikm_len = 22,
+               .salt = NULL,
+               .salt_len = 0,
+               .info = NULL,
+               .info_len = 0,
+               .okm_len = 42,
+               .okm =  "\x14\x07\xd4\x60\x13\xd9\x8b\xc6"
+                       "\xde\xce\xfc\xfe\xe5\x5f\x0f\x90"
+                       "\xb0\xc7\xf6\x3d\x68\xeb\x1a\x80"
+                       "\xea\xf0\x7e\x95\x3c\xfc\x0a\x3a"
+                       "\x52\x40\xa1\x55\xd6\xe4\xda\xa9"
+                       "\x65\xbb",
+       },
+};
+
+static void
+hexdump(char *str, uint8_t *src, uint_t len)
+{
+       int i;
+
+       printf("\t%s\t", str);
+       for (i = 0; i < len; i++) {
+               printf("%02x", src[i] & 0xff);
+       }
+       printf("\n");
+}
+
+static int
+run_test(int i, hkdf_tv_t *tv)
+{
+       int ret;
+       uint8_t okey[SHA512_DIGEST_LENGTH];
+
+       printf("TEST %d:\t", i);
+
+       ret = hkdf_sha512((uint8_t *)tv->ikm, tv->ikm_len, (uint8_t *)tv->salt,
+           tv->salt_len, (uint8_t *)tv->info, tv->info_len, okey, tv->okm_len);
+       if (ret != 0) {
+               printf("HKDF failed with error code %d\n", ret);
+               return (ret);
+       }
+
+       if (bcmp(okey, tv->okm, tv->okm_len) != 0) {
+               printf("Output Mismatch\n");
+               hexdump("Expected:", (uint8_t *)tv->okm, tv->okm_len);
+               hexdump("Actual:  ", okey, tv->okm_len);
+               return (1);
+       }
+
+       printf("Passed\n");
+
+       return (0);
+}
+
+int
+main(int argc, char **argv)
+{
+       int ret, i;
+
+       icp_init();
+
+       for (i = 0; i < NELEMS(test_vectors); i++) {
+               ret = run_test(i, &test_vectors[i]);
+               if (ret != 0)
+                       break;
+       }
+
+       icp_fini();
+
+       if (ret == 0) {
+               printf("All tests passed successfully.\n");
+               return (0);
+       } else {
+               printf("Test failed.\n");
+               return (1);
+       }
+}
diff --git a/tests/zfs-tests/tests/functional/hkdf/run_hkdf_test.ksh b/tests/zfs-tests/tests/functional/hkdf/run_hkdf_test.ksh
new file mode 100755 (executable)
index 0000000..5fde0b8
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/ksh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2017 by Datto Inc. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+#      Call the hkdf_test tool to test ZFS's HKDF implementation against
+#      a few test vectors.
+#
+
+log_assert "Run the tests for the HKDF algorithm."
+
+log_must $STF_SUITE/tests/functional/hkdf/hkdf_test
+
+log_pass "HKDF tests pass."
diff --git a/tests/zfs-tests/tests/functional/hkdf/setup.ksh b/tests/zfs-tests/tests/functional/hkdf/setup.ksh
new file mode 100755 (executable)
index 0000000..2bdca19
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/ksh
+
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2017 by Datto Inc. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "global"
+
+log_pass