This commit is contained in:
наб 2024-07-17 07:16:25 +08:00 committed by GitHub
commit 34d4266afd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 400 additions and 114 deletions

View File

@ -28,6 +28,7 @@ libmount-dev
libpam0g-dev
libselinux1-dev
libssl-dev
libargon2-dev
libtool
libudev-dev
linux-headers-generic

View File

@ -42,6 +42,7 @@
#include <stdlib.h>
#include <ctype.h>
#include <getopt.h>
#include <argon2.h>
#include <openssl/evp.h>
#include <sys/zfs_context.h>
#include <sys/spa.h>
@ -3053,7 +3054,7 @@ static char *key_material = NULL;
static boolean_t
zdb_derive_key(dsl_dir_t *dd, uint8_t *key_out)
{
uint64_t keyformat, salt, iters;
uint64_t keyformat, kdf = ZFS_PASSPHRASE_KDF_PBKDF2, salt, iters;
int i;
unsigned char c;
@ -3081,10 +3082,35 @@ zdb_derive_key(dsl_dir_t *dd, uint8_t *key_out)
dd->dd_crypto_obj, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS),
sizeof (uint64_t), 1, &iters));
if (PKCS5_PBKDF2_HMAC_SHA1(key_material, strlen(key_material),
((uint8_t *)&salt), sizeof (uint64_t), iters,
int err = zap_lookup(dd->dd_pool->dp_meta_objset,
dd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF),
sizeof (uint64_t), 1, &kdf);
VERIFY(err == 0 || err == ENOENT);
switch (kdf) {
case ZFS_PASSPHRASE_KDF_PBKDF2:
if (PKCS5_PBKDF2_HMAC_SHA1(key_material,
strlen(key_material), ((uint8_t *)&salt),
sizeof (uint64_t), iters,
WRAPPING_KEY_LEN, key_out) != 1)
return (B_FALSE);
break;
case ZFS_PASSPHRASE_KDF_ARGON2ID:
zfs_passphrase_kdf_argon2id_params_t params =
zfs_passphrase_kdf_argon2id_params(iters);
if (argon2_hash(params.t_cost, params.m_cost,
params.parallelism,
key_material, strlen(key_material),
&salt, sizeof (uint64_t), key_out, WRAPPING_KEY_LEN,
NULL, 0, Argon2_id, ARGON2_VERSION_13)
!= ARGON2_OK)
return (B_FALSE);
break;
default:
fatal("no support for KDF %u\n",
(unsigned int) kdf);
}
break;

View File

@ -416,8 +416,9 @@ get_usage(zfs_help_t idx)
"<-a | filesystem|volume>\n"));
case HELP_CHANGE_KEY:
return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n"
"\t [-o keylocation=<value>] [-o pbkdf2iters=<value>]\n"
"\t <filesystem|volume>\n"
"\t [-o keylocation=<value>] "
"[-o passphrasekdf=<value>] \n"
"\t [-o pbkdf2iters=<value>] <filesystem|volume>\n"
"\tchange-key -i [-l] <filesystem|volume>\n"));
case HELP_VERSION:
return (gettext("\tversion\n"));

View File

@ -4545,6 +4545,9 @@ ztest_dataset_create(char *dsname)
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_PASSPHRASE_KDF),
ztest_random(ZFS_PASSPHRASE_KDF_KDFS));
fnvlist_add_uint64(props,
zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), 0ULL);
fnvlist_add_uint64(props,

View File

@ -1,8 +1,16 @@
dnl #
dnl # Check for libcrypto. Used for userspace password derivation via PBKDF2.
dnl # Check for libcrypto and libargon. Used for userspace password derivation via PBKDF2 and argon2id.
dnl #
AC_DEFUN([ZFS_AC_CONFIG_USER_LIBCRYPTO], [
ZFS_AC_FIND_SYSTEM_LIBRARY(LIBCRYPTO, [libcrypto], [openssl/evp.h], [], [crypto], [PKCS5_PBKDF2_HMAC_SHA1], [], [
AC_MSG_FAILURE([
*** evp.h missing, libssl-devel package required])])
ZFS_AC_FIND_SYSTEM_LIBRARY(LIBCRYPTO_SSL, [libcrypto], [openssl/evp.h], [], [crypto], [PKCS5_PBKDF2_HMAC_SHA1], [], [
AC_MSG_FAILURE([*** evp.h missing, libssl-devel package required])])
# ARGON2 is included in openssl 3.2: once this is widely distributed, we should detect it and drop the libargon2 dep
ZFS_AC_FIND_SYSTEM_LIBRARY(LIBCRYPTO_ARGON2, [libargon2], [argon2.h], [], [argon2], [argon2id_hash_raw], [], [
AC_MSG_FAILURE([*** libargon2-dev package required])])
LIBCRYPTO_CFLAGS="$LIBCRYPTO_SSL_CFLAGS $LIBCRYPTO_ARGON2_CFLAGS"
LIBCRYPTO_LIBS="$LIBCRYPTO_SSL_LIBS $LIBCRYPTO_ARGON2_LIBS"
AC_SUBST(LIBCRYPTO_CFLAGS, [])
AC_SUBST(LIBCRYPTO_LIBS, [])
])

View File

@ -33,6 +33,7 @@
#include <sys/zio_crypt.h>
#include <openssl/evp.h>
#include <argon2.h>
#define PAM_SM_AUTH
#define PAM_SM_PASSWORD
@ -250,17 +251,24 @@ static pw_password_t *
prepare_passphrase(pam_handle_t *pamh, zfs_handle_t *ds,
const char *passphrase, nvlist_t *nvlist)
{
const char *errstr = NULL;
pw_password_t *key = alloc_pw_size(WRAPPING_KEY_LEN);
if (!key) {
return (NULL);
}
uint64_t salt;
uint64_t iters;
uint64_t kdf, salt, iters;
if (nvlist != NULL) {
kdf = ZFS_PASSPHRASE_KDF_PBKDF2;
if (nvlist_add_uint64(nvlist,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), kdf)) {
errstr = "failed to add KDF to nvlist";
goto err;
}
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
pw_free(key);
return (NULL);
errstr = "/dev/urandom";
goto err;
}
int bytes_read = 0;
char *buf = (char *)&salt;
@ -270,8 +278,8 @@ prepare_passphrase(pam_handle_t *pamh, zfs_handle_t *ds,
- bytes_read);
if (len < 0) {
close(fd);
pw_free(key);
return (NULL);
errstr = "failed to read salt";
goto err;
}
bytes_read += len;
}
@ -279,34 +287,54 @@ prepare_passphrase(pam_handle_t *pamh, zfs_handle_t *ds,
if (nvlist_add_uint64(nvlist,
zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), salt)) {
pam_syslog(pamh, LOG_ERR,
"failed to add salt to nvlist");
pw_free(key);
return (NULL);
errstr = "failed to add salt to nvlist";
goto err;
}
iters = DEFAULT_PBKDF2_ITERATIONS;
iters = zfs_passphrase_kdf_default_parameters[kdf];
if (nvlist_add_uint64(nvlist, zfs_prop_to_name(
ZFS_PROP_PBKDF2_ITERS), iters)) {
pam_syslog(pamh, LOG_ERR,
"failed to add iters to nvlist");
pw_free(key);
return (NULL);
errstr = "failed to add iters to nvlist";
goto err;
}
} else {
kdf = zfs_prop_get_int(ds, ZFS_PROP_PASSPHRASE_KDF);
salt = zfs_prop_get_int(ds, ZFS_PROP_PBKDF2_SALT);
iters = zfs_prop_get_int(ds, ZFS_PROP_PBKDF2_ITERS);
}
salt = LE_64(salt);
if (!PKCS5_PBKDF2_HMAC_SHA1((char *)passphrase,
strlen(passphrase), (uint8_t *)&salt,
switch (kdf) {
case ZFS_PASSPHRASE_KDF_PBKDF2:
if (PKCS5_PBKDF2_HMAC_SHA1((char *)passphrase,
strlen(passphrase), ((uint8_t *)&salt),
sizeof (uint64_t), iters, WRAPPING_KEY_LEN,
(uint8_t *)key->value)) {
pam_syslog(pamh, LOG_ERR, "pbkdf failed");
(uint8_t *)key->value) != 1)
errstr = "PBKDF2 failed";
break;
case ZFS_PASSPHRASE_KDF_ARGON2ID:
zfs_passphrase_kdf_argon2id_params_t params =
zfs_passphrase_kdf_argon2id_params(iters);
if (argon2_hash(params.t_cost, params.m_cost,
params.parallelism,
passphrase, strlen((char *)passphrase),
&salt, sizeof (uint64_t), (uint8_t *)key->value,
WRAPPING_KEY_LEN, NULL, 0, Argon2_id, ARGON2_VERSION_13)
!= ARGON2_OK)
errstr = "ARGON2ID13 failed";
break;
default:
errstr = "unknown KDF";
break;
}
if (errstr)
goto err;
return (key);
err:
pam_syslog(pamh, LOG_ERR, "%s", errstr);
pw_free(key);
return (NULL);
}
return (key);
}
static int

View File

@ -836,8 +836,8 @@ def lzc_change_key(fsname, crypt_cmd, props=None, key=None):
supported values are "new_key", "inherit", "force_new_key" and
"force_inherit".
:param props: a `dict` of encryption-related property name-value pairs;
only "keyformat", "keylocation" and "pbkdf2iters" are supported
(empty by default).
only "keyformat", "keylocation", "passphrasepkdf", and "pbkdf2iters"
are supported (empty by default).
:type props: dict of bytes:Any
:param key: dataset encryption key data (empty by default).
:type key: bytes

View File

@ -28,8 +28,9 @@
/*
* ZAP entry keys for DSL Crypto Keys stored on disk. In addition,
* ZFS_PROP_KEYFORMAT, ZFS_PROP_PBKDF2_SALT, and ZFS_PROP_PBKDF2_ITERS are
* also maintained here using their respective property names.
* ZFS_PROP_KEYFORMAT, ZFS_PROP_PBKDF2_SALT, ZFS_PROP_PBKDF2_ITERS,
* and ZFS_PROP_PASSPHRASE_KDF are also maintained here using their
* respective property names.
*/
#define DSL_CRYPTO_KEY_CRYPTO_SUITE "DSL_CRYPTO_SUITE"
#define DSL_CRYPTO_KEY_GUID "DSL_CRYPTO_GUID"
@ -52,10 +53,13 @@ typedef struct dsl_wrapping_key {
/* keyformat property enum */
zfs_keyformat_t wk_keyformat;
/* the pbkdf2 salt, if the keyformat is of type passphrase */
/* the KDF to use, if the keyformat is of type passphrase */
zfs_passphrase_kdf_t wk_kdf;
/* the KDF salt, if the keyformat is of type passphrase */
uint64_t wk_salt;
/* the pbkdf2 iterations, if the keyformat is of type passphrase */
/* the KDF iterations, if the keyformat is of type passphrase */
uint64_t wk_iters;
/* actual wrapping key */

View File

@ -193,6 +193,7 @@ typedef enum {
ZFS_PROP_SNAPSHOTS_CHANGED,
ZFS_PROP_PREFETCH,
ZFS_PROP_VOLTHREADING,
ZFS_PROP_PASSPHRASE_KDF,
ZFS_NUM_PROPS
} zfs_prop_t;
@ -541,6 +542,52 @@ typedef enum zfs_keyformat {
ZFS_KEYFORMAT_FORMATS
} zfs_keyformat_t;
typedef enum zfs_passphrase_kdf {
ZFS_PASSPHRASE_KDF_PBKDF2 = 0,
ZFS_PASSPHRASE_KDF_ARGON2ID,
ZFS_PASSPHRASE_KDF_KDFS
} zfs_passphrase_kdf_t;
typedef struct zfs_passphrase_kdf_argon2id_params {
uint32_t t_cost;
uint32_t m_cost;
uint32_t parallelism;
} zfs_passphrase_kdf_argon2id_params_t;
static zfs_passphrase_kdf_argon2id_params_t
zfs_passphrase_kdf_argon2id_params(uint64_t iters)
{
struct zfs_passphrase_kdf_argon2id_params ret = {
.t_cost = (iters >> (16 * 2)) & 0xFFFF,
.m_cost = (iters >> (16 * 1)) & 0xFFFF,
.parallelism = (iters >> (16 * 0)) & 0xFFFF,
};
return (ret);
}
static boolean_t
zfs_passphrase_kdf_pbkdf2_min_validate(uint64_t iters)
{
return (iters >= 100000);
}
static boolean_t
zfs_passphrase_kdf_argon2id_min_validate(uint64_t iters)
{
zfs_passphrase_kdf_argon2id_params_t p =
zfs_passphrase_kdf_argon2id_params(iters);
return (!(iters & 0xFFFF000000000000ull) &&
p.t_cost >= 1 && p.m_cost >= 12 && p.parallelism >= 1);
}
static const uint64_t zfs_passphrase_kdf_default_parameters[
ZFS_PASSPHRASE_KDF_KDFS] = {350000, 0x000100110001ull /* m=17 */};
static boolean_t(*const zfs_passphrase_kdf_min_parameters[
ZFS_PASSPHRASE_KDF_KDFS])(uint64_t) = {
zfs_passphrase_kdf_pbkdf2_min_validate,
zfs_passphrase_kdf_argon2id_min_validate};
static const char * const zfs_passphrase_kdf_min_parameters_str[
ZFS_PASSPHRASE_KDF_KDFS] = {"100000", "0x0001000C0001"};
typedef enum zfs_key_location {
ZFS_KEYLOCATION_NONE = 0,
ZFS_KEYLOCATION_PROMPT,
@ -554,9 +601,6 @@ typedef enum {
ZFS_PREFETCH_ALL = 2
} zfs_prefetch_type_t;
#define DEFAULT_PBKDF2_ITERATIONS 350000
#define MIN_PBKDF2_ITERATIONS 100000
/*
* On-disk version number.
*/

View File

@ -1847,7 +1847,8 @@
<enumerator name='ZFS_PROP_SNAPSHOTS_CHANGED' value='95'/>
<enumerator name='ZFS_PROP_PREFETCH' value='96'/>
<enumerator name='ZFS_PROP_VOLTHREADING' value='97'/>
<enumerator name='ZFS_NUM_PROPS' value='98'/>
<enumerator name='ZFS_PROP_PASSPHRASE_KDF' value='98'/>
<enumerator name='ZFS_NUM_PROPS' value='99'/>
</enum-decl>
<typedef-decl name='zfs_prop_t' type-id='4b000d60' id='58603c44'/>
<enum-decl name='zprop_source_t' naming-typedef-id='a2256d42' id='5903f80e'>

View File

@ -26,6 +26,7 @@
#include <signal.h>
#include <errno.h>
#include <openssl/evp.h>
#include <argon2.h>
#if LIBFETCH_DYNAMIC
#include <dlfcn.h>
#endif
@ -774,12 +775,14 @@ error:
}
static int
derive_key(libzfs_handle_t *hdl, zfs_keyformat_t format, uint64_t iters,
uint8_t *key_material, uint64_t salt,
uint8_t **key_out)
derive_key(libzfs_handle_t *hdl, zfs_keyformat_t format,
zfs_passphrase_kdf_t kdf, uint64_t iters, uint8_t *key_material,
uint64_t salt, uint8_t **key_out)
{
int ret;
uint8_t *key;
boolean_t err;
zfs_passphrase_kdf_argon2id_params_t params;
*key_out = NULL;
@ -801,10 +804,28 @@ derive_key(libzfs_handle_t *hdl, zfs_keyformat_t format, uint64_t iters,
case ZFS_KEYFORMAT_PASSPHRASE:
salt = LE_64(salt);
ret = PKCS5_PBKDF2_HMAC_SHA1((char *)key_material,
ret = 0;
switch (kdf) {
case ZFS_PASSPHRASE_KDF_PBKDF2:
err = 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) {
sizeof (uint64_t), iters, WRAPPING_KEY_LEN, key)
!= 1;
break;
case ZFS_PASSPHRASE_KDF_ARGON2ID:
params = zfs_passphrase_kdf_argon2id_params(iters);
err = argon2_hash(params.t_cost, params.m_cost,
params.parallelism,
key_material, strlen((char *)key_material),
&salt, sizeof (uint64_t), key, WRAPPING_KEY_LEN,
NULL, 0, Argon2_id, ARGON2_VERSION_13)
!= ARGON2_OK;
break;
default:
ret = EINVAL;
goto error;
}
if (err) {
ret = EIO;
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"Failed to generate key from passphrase."));
@ -850,8 +871,8 @@ encryption_feature_is_enabled(zpool_handle_t *zph)
static int
populate_create_encryption_params_nvlists(libzfs_handle_t *hdl,
zfs_handle_t *zhp, boolean_t newkey, zfs_keyformat_t keyformat,
const char *keylocation, nvlist_t *props, uint8_t **wkeydata,
uint_t *wkeylen)
const char *keylocation, zfs_passphrase_kdf_t kdf, nvlist_t *props,
uint8_t **wkeydata, uint_t *wkeylen)
{
int ret;
uint64_t iters = 0, salt = 0;
@ -893,7 +914,7 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl,
ret = nvlist_lookup_uint64(props,
zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &iters);
if (ret == ENOENT) {
iters = DEFAULT_PBKDF2_ITERATIONS;
iters = zfs_passphrase_kdf_default_parameters[kdf];
ret = nvlist_add_uint64(props,
zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), iters);
if (ret != 0)
@ -917,7 +938,8 @@ populate_create_encryption_params_nvlists(libzfs_handle_t *hdl,
}
/* derive a key from the key material */
ret = derive_key(hdl, keyformat, iters, key_material, salt, &key_data);
ret = derive_key(hdl, keyformat, kdf, iters, key_material, salt,
&key_data);
if (ret != 0)
goto error;
@ -955,15 +977,14 @@ proplist_has_encryption_props(nvlist_t *props)
if (ret == 0 && strcmp(strval, "none") != 0)
return (B_TRUE);
zfs_prop_t exist_props[] = {
ZFS_PROP_KEYFORMAT, ZFS_PROP_PASSPHRASE_KDF, ZFS_PROP_PBKDF2_ITERS};
for (size_t i = 0; i < ARRAY_SIZE(exist_props); ++i) {
ret = nvlist_lookup_uint64(props,
zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &intval);
if (ret == 0)
return (B_TRUE);
ret = nvlist_lookup_uint64(props,
zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), &intval);
zfs_prop_to_name(exist_props[i]), &intval);
if (ret == 0)
return (B_TRUE);
}
return (B_FALSE);
}
@ -1008,6 +1029,7 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props,
char errbuf[ERRBUFLEN];
uint64_t crypt = ZIO_CRYPT_INHERIT, pcrypt = ZIO_CRYPT_INHERIT;
uint64_t keyformat = ZFS_KEYFORMAT_NONE;
uint64_t kdf = ZFS_PASSPHRASE_KDF_PBKDF2;
const char *keylocation = NULL;
zfs_handle_t *pzhp = NULL;
uint8_t *wkeydata = NULL;
@ -1026,6 +1048,8 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props,
/* lookup key location and format from props */
(void) nvlist_lookup_uint64(props,
zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat);
(void) nvlist_lookup_uint64(props,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), &kdf);
(void) nvlist_lookup_string(props,
zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation);
@ -1147,7 +1171,7 @@ zfs_crypto_create(libzfs_handle_t *hdl, char *parent_name, nvlist_t *props,
}
ret = populate_create_encryption_params_nvlists(hdl, NULL,
B_TRUE, keyformat, keylocation, props, &wkeydata,
B_TRUE, keyformat, keylocation, kdf, props, &wkeydata,
&wkeylen);
if (ret != 0)
goto out;
@ -1186,6 +1210,7 @@ zfs_crypto_clone_check(libzfs_handle_t *hdl, zfs_handle_t *origin_zhp,
* inherited from the origin dataset.
*/
if (nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_KEYFORMAT)) ||
nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF)) ||
nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_KEYLOCATION)) ||
nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION)) ||
nvlist_exists(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS))) {
@ -1280,6 +1305,7 @@ zfs_crypto_load_key(zfs_handle_t *zhp, boolean_t noop,
int ret, attempts = 0;
char errbuf[ERRBUFLEN];
uint64_t keystatus, iters = 0, salt = 0;
uint64_t kdf = ZFS_PASSPHRASE_KDF_PBKDF2;
uint64_t keyformat = ZFS_KEYFORMAT_NONE;
char prop_keylocation[MAXNAMELEN];
char prop_encroot[MAXNAMELEN];
@ -1356,8 +1382,9 @@ zfs_crypto_load_key(zfs_handle_t *zhp, boolean_t noop,
}
}
/* passphrase formats require a salt and pbkdf2_iters property */
/* passphrase formats require a kdf, salt, and pbkdf2_iters property */
if (keyformat == ZFS_KEYFORMAT_PASSPHRASE) {
kdf = zfs_prop_get_int(zhp, ZFS_PROP_PASSPHRASE_KDF);
salt = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_SALT);
iters = zfs_prop_get_int(zhp, ZFS_PROP_PBKDF2_ITERS);
}
@ -1374,8 +1401,8 @@ try_again:
goto error;
/* derive a key from the key material */
ret = derive_key(zhp->zfs_hdl, keyformat, iters, key_material, salt,
&key_data);
ret = derive_key(zhp->zfs_hdl, keyformat, kdf, iters, key_material,
salt, &key_data);
if (ret != 0)
goto error;
@ -1556,12 +1583,13 @@ zfs_crypto_verify_rewrap_nvlist(zfs_handle_t *zhp, nvlist_t *props,
case ZFS_PROP_PBKDF2_ITERS:
case ZFS_PROP_KEYFORMAT:
case ZFS_PROP_KEYLOCATION:
case ZFS_PROP_PASSPHRASE_KDF:
break;
default:
ret = EINVAL;
zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
"Only keyformat, keylocation and pbkdf2iters may "
"be set with this command."));
"Only keyformat, keylocation, passphrasekdf and "
"pbkdf2iters may be set with this command."));
goto error;
}
}
@ -1588,13 +1616,14 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
{
int ret;
char errbuf[ERRBUFLEN];
boolean_t is_encroot;
boolean_t is_encroot, explicit_kdf;
nvlist_t *props = NULL;
uint8_t *wkeydata = NULL;
uint_t wkeylen = 0;
dcp_cmd_t cmd = (inheritkey) ? DCP_CMD_INHERIT : DCP_CMD_NEW_KEY;
uint64_t crypt, pcrypt, keystatus, pkeystatus;
uint64_t keyformat = ZFS_KEYFORMAT_NONE;
uint64_t kdf;
zfs_handle_t *pzhp = NULL;
const char *keylocation = NULL;
char origin_name[MAXNAMELEN];
@ -1659,6 +1688,8 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat);
(void) nvlist_lookup_string(props,
zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation);
explicit_kdf = nvlist_lookup_uint64(props,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), &kdf) == 0;
if (is_encroot) {
/*
@ -1694,6 +1725,21 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
keylocation = prop_keylocation;
}
if (!explicit_kdf) {
kdf = zfs_prop_get_int(zhp,
ZFS_PROP_PASSPHRASE_KDF);
ret = nvlist_add_uint64(props,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF),
kdf);
if (ret != 0) {
zfs_error_aux(zhp->zfs_hdl,
dgettext(TEXT_DOMAIN, "Failed to "
"get existing passphrasekdf "
"property."));
goto error;
}
}
} else {
/* need a new key for non-encryption roots */
if (keyformat == ZFS_KEYFORMAT_NONE) {
@ -1713,11 +1759,13 @@ zfs_crypto_rewrap(zfs_handle_t *zhp, nvlist_t *raw_props, boolean_t inheritkey)
if (ret != 0)
goto error;
}
kdf = zfs_prop_get_int(zhp, ZFS_PROP_PASSPHRASE_KDF);
}
/* fetch the new wrapping key and associated properties */
ret = populate_create_encryption_params_nvlists(zhp->zfs_hdl,
zhp, B_TRUE, keyformat, keylocation, props, &wkeydata,
zhp, B_TRUE, keyformat, keylocation, kdf, props, &wkeydata,
&wkeylen);
if (ret != 0)
goto error;

View File

@ -1039,6 +1039,9 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
int chosen_normal = -1;
int chosen_utf = -1;
int set_maxbs = 0;
zfs_passphrase_kdf_t chosen_kdf = ZFS_PASSPHRASE_KDF_PBKDF2;
uint64_t chosen_iters;
boolean_t have_chosen_iters = B_FALSE;
if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
(void) no_memory(hdl);
@ -1507,14 +1510,13 @@ badlabel:
}
break;
case ZFS_PROP_PASSPHRASE_KDF:
chosen_kdf = intval;
break;
case ZFS_PROP_PBKDF2_ITERS:
if (intval < MIN_PBKDF2_ITERATIONS) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"minimum pbkdf2 iterations is %u"),
MIN_PBKDF2_ITERATIONS);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
}
chosen_iters = intval;
have_chosen_iters = B_TRUE;
break;
case ZFS_PROP_UTF8ONLY:
@ -1589,6 +1591,15 @@ badlabel:
}
}
if (have_chosen_iters &&
!zfs_passphrase_kdf_min_parameters[chosen_kdf](chosen_iters)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"minimum KDF iterations is %s"),
zfs_passphrase_kdf_min_parameters_str[chosen_kdf]);
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
goto error;
}
/*
* If normalization was chosen, but no UTF8 choice was made,
* enforce rejection of non-UTF8 names.

View File

@ -38,7 +38,7 @@
.\" Copyright (c) 2019, Kjeld Schouten-Lebbing
.\" Copyright (c) 2022 Hewlett Packard Enterprise Development LP.
.\"
.Dd August 8, 2023
.Dd February 8, 2024
.Dt ZFSPROPS 7
.Os
.
@ -1110,7 +1110,7 @@ A raw key can be generated with the following command:
.Dl # Nm dd Sy if=/dev/urandom bs=32 count=1 Sy of= Ns Pa /path/to/output/key
.Pp
Passphrases must be between 8 and 512 bytes long and will be processed through
PBKDF2 before being used (see the
a KDF before being used (see the
.Sy pbkdf2iters
property).
Even though the encryption suite cannot be changed after dataset creation,
@ -1163,14 +1163,27 @@ and
.Sy SSL_CLIENT_KEY_FILE
environment variables can be set to configure the path
to the client certificate and its key.
.It Sy pbkdf2iters Ns = Ns Ar iterations
Controls the number of PBKDF2 iterations that a
.It Sy passphrasekdf Ns = Ns Sy pbkdf2 Ns | Ns Sy argon2id
If keyformat is
.Sy passphrase ,
this variable governs which key derivation function will be used (see below).
The default value is
.Sy pbkdf2
(PBKDF2).
Datasets using
.Sy argon2id
(Argon2id version 13)
cannot be unlocked with OpenZFS <2.2.3.
This property may be changed with
.Nm zfs Cm change-key .
.It Sy pbkdf2iters Ns = Ns Ar parameters Pq Sy kdfparams Ns = Ns Ar parameters
Controls KDF parameters that a
.Sy passphrase
encryption key should be run through when processing it into an encryption key.
This property is only defined when encryption is enabled and a keyformat of
.Sy passphrase
is selected.
The goal of PBKDF2 is to significantly increase the
The goal of a KDF is to significantly increase the
computational difficulty needed to brute force a user's passphrase.
This is accomplished by forcing the attacker to run each passphrase through a
computationally expensive hashing function many times before they arrive at the
@ -1178,12 +1191,24 @@ resulting key.
A user who actually knows the passphrase will only have to pay this cost once.
As CPUs become better at processing, this number should be
raised to ensure that a brute force attack is still not possible.
The current default is
.Sy 350000
and the minimum is
.Sy 100000 .
This property may be changed with
.Nm zfs Cm change-key .
.Pp
For PBKDF2, the only parameter is the iteration count (default
.Sy 350000 ,
minimum
.Sy 10000 ) .
.br
For Argon2id, this parameter is a bitfield of three 16-bit integers:
the most significant is the iteration count,
then the memory cost (log2 kilobytes),
and the least significant is the parallelism.
The default is
.Sy 0x000100110001
.Pq Va t Ns = Ns Sy 1 , Va m Ns = Ns Sy 17 Po 128M Pc , Va p Ns = Ns Sy 1 ,
the minimum \(en
.Sy 0x0001000C0001
.Pq Va t Ns = Ns Sy 1 , Va m Ns = Ns Sy 12 Po 4M Pc , Va p Ns = Ns Sy 1 .
.It Sy exec Ns = Ns Sy on Ns | Ns Sy off
Controls whether processes can be executed from within this file system.
The default value is
@ -1267,10 +1292,10 @@ location.
When the
.Sy mountpoint
property is set with
.Nm zfs Cm set Fl u
, the
.Nm zfs Cm set Fl u ,
the
.Sy mountpoint
property is updated but dataset is not mounted or unmounted and remains
property is updated but the dataset is neither mounted nor unmounted and remains
as it was before.
.It Sy nbmand Ns = Ns Sy on Ns | Ns Sy off
Controls whether the file system should be mounted with

View File

@ -29,7 +29,7 @@
.\" Copyright 2018 Nexenta Systems, Inc.
.\" Copyright 2019 Joyent, Inc.
.\"
.Dd March 16, 2022
.Dd December 17, 2023
.Dt ZFS-ALLOW 8
.Os
.
@ -249,6 +249,7 @@ filesystem_limit property
fscontext property
keyformat property
keylocation property
passphrasekdf property
logbias property
mlslabel property
mountpoint property

View File

@ -29,7 +29,7 @@
.\" Copyright 2018 Nexenta Systems, Inc.
.\" Copyright 2019 Joyent, Inc.
.\"
.Dd January 13, 2020
.Dd December 17, 2023
.Dt ZFS-LOAD-KEY 8
.Os
.
@ -51,6 +51,7 @@
.Op Fl l
.Op Fl o Ar keylocation Ns = Ns Ar value
.Op Fl o Ar keyformat Ns = Ns Ar value
.Op Fl o Ar passphrasekdf Ns = Ns Ar value
.Op Fl o Ar pbkdf2iters Ns = Ns Ar value
.Ar filesystem
.Nm zfs
@ -83,6 +84,9 @@ Note that if the
is set to
.Sy prompt
the terminal will interactively wait for the key to be entered.
If
.Sy passphrasekdf
isn't specified, the current value is preserved.
Loading a key will not automatically mount the dataset.
If that functionality is desired,
.Nm zfs Cm mount Fl l
@ -152,6 +156,7 @@ Unloads the keys for all encryption roots in all imported pools.
.Op Fl l
.Op Fl o Ar keylocation Ns = Ns Ar value
.Op Fl o Ar keyformat Ns = Ns Ar value
.Op Fl o Ar passphrasekdf Ns = Ns Ar value
.Op Fl o Ar pbkdf2iters Ns = Ns Ar value
.Ar filesystem
.Xc
@ -167,6 +172,7 @@ This command requires that the existing key for the dataset is already loaded.
This command may also be used to change the
.Sy keylocation ,
.Sy keyformat ,
.Sy passphrasekdf ,
and
.Sy pbkdf2iters
properties as needed.
@ -204,10 +210,11 @@ This is effectively equivalent to running
.Nm zfs Cm load-key Ar filesystem ; Nm zfs Cm change-key Ar filesystem
.It Fl o Ar property Ns = Ns Ar value
Allows the user to set encryption key properties
.Pq Sy keyformat , keylocation , No and Sy pbkdf2iters
.Pq Sy keyformat , keylocation , passphrasekdf , No and Sy pbkdf2iters
while changing the key.
This is the only way to alter
.Sy keyformat
.Sy keyformat ,
.Sy passphrasekdf ,
and
.Sy pbkdf2iters
after the dataset has been created.
@ -244,7 +251,7 @@ subcommand for more info on key loading).
Creating an encrypted dataset requires specifying the
.Sy encryption No and Sy keyformat
properties at creation time, along with an optional
.Sy keylocation No and Sy pbkdf2iters .
.Sy keylocation , passphrasekdf , No and Sy pbkdf2iters .
After entering an encryption key, the
created dataset will become an encryption root.
Any descendant datasets will
@ -265,7 +272,7 @@ 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
.Pq namely Sy keystatus , keyformat , keylocation , No and Sy pbkdf2iters
.Pq namely Sy keystatus , keyformat , passphrasekdf , keylocation , No 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

View File

@ -234,6 +234,12 @@ zfs_prop_init(void)
{ NULL }
};
static const zprop_index_t passphrase_kdf_table[] = {
{ "pbkdf2", ZFS_PASSPHRASE_KDF_PBKDF2 },
{ "argon2id", ZFS_PASSPHRASE_KDF_ARGON2ID },
{ NULL }
};
static const zprop_index_t snapdir_table[] = {
{ "hidden", ZFS_SNAPDIR_HIDDEN },
{ "visible", ZFS_SNAPDIR_VISIBLE },
@ -554,6 +560,11 @@ zfs_prop_init(void)
"on | off | aes-128-ccm | aes-192-ccm | aes-256-ccm | "
"aes-128-gcm | aes-192-gcm | aes-256-gcm", "ENCRYPTION",
crypto_table, sfeatures);
zprop_register_index(ZFS_PROP_PASSPHRASE_KDF, "passphrasekdf",
ZFS_PASSPHRASE_KDF_PBKDF2, PROP_ONETIME_DEFAULT,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
"pbkdf2 | argon2id", "KDF", passphrase_kdf_table,
sfeatures);
/* set once index (boolean) properties */
zprop_register_index(ZFS_PROP_UTF8ONLY, "utf8only", 0, PROP_ONETIME,
@ -668,7 +679,7 @@ zfs_prop_init(void)
B_TRUE, sfeatures);
zprop_register_number(ZFS_PROP_PBKDF2_ITERS, "pbkdf2iters",
0, PROP_ONETIME_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
"<iters>", "PBKDF2ITERS", B_TRUE, sfeatures);
"<iters>", "KDFPARAMS", B_TRUE, sfeatures);
zprop_register_number(ZFS_PROP_OBJSETID, "objsetid", 0,
PROP_READONLY, ZFS_TYPE_DATASET, "<uint64>", "OBJSETID", B_TRUE,
sfeatures);
@ -838,10 +849,10 @@ zfs_prop_userquota(const char *name)
boolean_t
zfs_prop_written(const char *name)
{
static const char *prop_prefix = "written@";
static const char *book_prefix = "written#";
return (strncmp(name, prop_prefix, strlen(prop_prefix)) == 0 ||
strncmp(name, book_prefix, strlen(book_prefix)) == 0);
static const char prop_prefix[] = "written@";
static const char book_prefix[] = "written#";
return (strncmp(name, prop_prefix, sizeof (prop_prefix) - 1) == 0 ||
strncmp(name, book_prefix, sizeof (book_prefix) - 1) == 0);
}
/*
@ -972,7 +983,7 @@ zfs_prop_encryption_key_param(zfs_prop_t prop)
* changed at will without needing the master keys.
*/
return (prop == ZFS_PROP_PBKDF2_SALT || prop == ZFS_PROP_PBKDF2_ITERS ||
prop == ZFS_PROP_KEYFORMAT);
prop == ZFS_PROP_KEYFORMAT || prop == ZFS_PROP_PASSPHRASE_KDF);
}
/*

View File

@ -109,7 +109,8 @@ dsl_wrapping_key_free(dsl_wrapping_key_t *wkey)
static void
dsl_wrapping_key_create(uint8_t *wkeydata, zfs_keyformat_t keyformat,
uint64_t salt, uint64_t iters, dsl_wrapping_key_t **wkey_out)
zfs_passphrase_kdf_t kdf, uint64_t salt, uint64_t iters,
dsl_wrapping_key_t **wkey_out)
{
dsl_wrapping_key_t *wkey;
@ -125,6 +126,7 @@ dsl_wrapping_key_create(uint8_t *wkeydata, zfs_keyformat_t keyformat,
/* initialize the rest of the struct */
zfs_refcount_create(&wkey->wk_refcnt);
wkey->wk_keyformat = keyformat;
wkey->wk_kdf = kdf;
wkey->wk_salt = salt;
wkey->wk_iters = iters;
@ -138,6 +140,7 @@ dsl_crypto_params_create_nvlist(dcp_cmd_t cmd, nvlist_t *props,
int ret;
uint64_t crypt = ZIO_CRYPT_INHERIT;
uint64_t keyformat = ZFS_KEYFORMAT_NONE;
uint64_t kdf = ZFS_PASSPHRASE_KDF_PBKDF2;
uint64_t salt = 0, iters = 0;
dsl_crypto_params_t *dcp = NULL;
dsl_wrapping_key_t *wkey = NULL;
@ -156,6 +159,8 @@ dsl_crypto_params_create_nvlist(dcp_cmd_t cmd, nvlist_t *props,
zfs_prop_to_name(ZFS_PROP_KEYFORMAT), &keyformat);
(void) nvlist_lookup_string(props,
zfs_prop_to_name(ZFS_PROP_KEYLOCATION), &keylocation);
(void) nvlist_lookup_uint64(props,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), &kdf);
(void) nvlist_lookup_uint64(props,
zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), &salt);
(void) nvlist_lookup_uint64(props,
@ -191,6 +196,12 @@ dsl_crypto_params_create_nvlist(dcp_cmd_t cmd, nvlist_t *props,
goto error;
}
/* check for valid kdf */
if (kdf >= ZFS_PASSPHRASE_KDF_KDFS) {
ret = SET_ERROR(EINVAL);
goto error;
}
/* check for a valid keylocation (of any kind) and copy it in */
if (keylocation != NULL) {
if (!zfs_prop_valid_keylocation(keylocation, B_FALSE)) {
@ -214,7 +225,7 @@ dsl_crypto_params_create_nvlist(dcp_cmd_t cmd, nvlist_t *props,
/* create the wrapping key from the raw data */
if (wkeydata != NULL) {
/* create the wrapping key with the verified parameters */
dsl_wrapping_key_create(wkeydata, keyformat, salt,
dsl_wrapping_key_create(wkeydata, keyformat, kdf, salt,
iters, &wkey);
dcp->cp_wkey = wkey;
}
@ -225,6 +236,8 @@ dsl_crypto_params_create_nvlist(dcp_cmd_t cmd, nvlist_t *props,
*/
(void) nvlist_remove_all(props, zfs_prop_to_name(ZFS_PROP_ENCRYPTION));
(void) nvlist_remove_all(props, zfs_prop_to_name(ZFS_PROP_KEYFORMAT));
(void) nvlist_remove_all(props,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF));
(void) nvlist_remove_all(props, zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT));
(void) nvlist_remove_all(props,
zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS));
@ -776,7 +789,7 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp,
dsl_crypto_key_t *dck = NULL;
dsl_wrapping_key_t *wkey = dcp->cp_wkey;
dsl_pool_t *dp = NULL;
uint64_t rddobj, keyformat, salt, iters;
uint64_t rddobj, keyformat, kdf, salt, iters;
/*
* We don't validate the wrapping key's keyformat, salt, or iters
@ -826,6 +839,13 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp,
if (ret != 0)
goto error;
ret = zap_lookup(dp->dp_meta_objset, dd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), 8, 1, &kdf);
if (ret == ENOENT)
kdf = ZFS_PASSPHRASE_KDF_PBKDF2;
else if (ret != 0)
goto error;
ret = zap_lookup(dp->dp_meta_objset, dd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), 8, 1, &salt);
if (ret != 0)
@ -838,6 +858,7 @@ spa_keystore_load_wkey(const char *dsname, dsl_crypto_params_t *dcp,
ASSERT3U(keyformat, <, ZFS_KEYFORMAT_FORMATS);
ASSERT3U(keyformat, !=, ZFS_KEYFORMAT_NONE);
ASSERT3U(kdf, <, ZFS_PASSPHRASE_KDF_KDFS);
IMPLY(keyformat == ZFS_KEYFORMAT_PASSPHRASE, iters != 0);
IMPLY(keyformat == ZFS_KEYFORMAT_PASSPHRASE, salt != 0);
IMPLY(keyformat != ZFS_KEYFORMAT_PASSPHRASE, iters == 0);
@ -1204,7 +1225,7 @@ static void
dsl_crypto_key_sync_impl(objset_t *mos, uint64_t dckobj, uint64_t crypt,
uint64_t root_ddobj, uint64_t guid, uint8_t *iv, uint8_t *mac,
uint8_t *keydata, uint8_t *hmac_keydata, uint64_t keyformat,
uint64_t salt, uint64_t iters, dmu_tx_t *tx)
uint64_t kdf, uint64_t salt, uint64_t iters, dmu_tx_t *tx)
{
VERIFY0(zap_update(mos, dckobj, DSL_CRYPTO_KEY_CRYPTO_SUITE, 8, 1,
&crypt, tx));
@ -1222,6 +1243,9 @@ dsl_crypto_key_sync_impl(objset_t *mos, uint64_t dckobj, uint64_t crypt,
SHA512_HMAC_KEYLEN, hmac_keydata, tx));
VERIFY0(zap_update(mos, dckobj, zfs_prop_to_name(ZFS_PROP_KEYFORMAT),
8, 1, &keyformat, tx));
VERIFY0(zap_update(mos, dckobj,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF),
8, 1, &kdf, tx));
VERIFY0(zap_update(mos, dckobj, zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT),
8, 1, &salt, tx));
VERIFY0(zap_update(mos, dckobj, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS),
@ -1248,8 +1272,8 @@ dsl_crypto_key_sync(dsl_crypto_key_t *dck, dmu_tx_t *tx)
/* update the ZAP with the obtained values */
dsl_crypto_key_sync_impl(tx->tx_pool->dp_meta_objset, dck->dck_obj,
key->zk_crypt, wkey->wk_ddobj, key->zk_guid, iv, mac, keydata,
hmac_keydata, wkey->wk_keyformat, wkey->wk_salt, wkey->wk_iters,
tx);
hmac_keydata, wkey->wk_keyformat, wkey->wk_kdf, wkey->wk_salt,
wkey->wk_iters, tx);
}
typedef struct spa_keystore_change_key_args {
@ -1403,7 +1427,8 @@ spa_keystore_change_key_check(void *arg, dmu_tx_t *tx)
/* passphrases require pbkdf2 salt and iters */
if (dcp->cp_wkey->wk_keyformat == ZFS_KEYFORMAT_PASSPHRASE) {
if (dcp->cp_wkey->wk_salt == 0 ||
dcp->cp_wkey->wk_iters < MIN_PBKDF2_ITERATIONS) {
!zfs_passphrase_kdf_min_parameters[
dcp->cp_wkey->wk_kdf](dcp->cp_wkey->wk_iters)) {
ret = SET_ERROR(EINVAL);
goto error;
}
@ -1889,7 +1914,8 @@ dmu_objset_create_crypt_check(dsl_dir_t *parentdd, dsl_crypto_params_t *dcp,
case ZFS_KEYFORMAT_PASSPHRASE:
/* requires pbkdf2 iters and salt */
if (dcp->cp_wkey->wk_salt == 0 ||
dcp->cp_wkey->wk_iters < MIN_PBKDF2_ITERATIONS)
!zfs_passphrase_kdf_min_parameters[
dcp->cp_wkey->wk_kdf](dcp->cp_wkey->wk_iters))
return (SET_ERROR(EINVAL));
break;
case ZFS_KEYFORMAT_NONE:
@ -2244,6 +2270,13 @@ dsl_crypto_recv_raw_key_check(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
is_passphrase = (intval == ZFS_KEYFORMAT_PASSPHRASE);
ret = nvlist_lookup_uint64(nvl,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), &intval);
if (ret == ENOENT)
;
else if (ret != 0 || intval >= ZFS_PASSPHRASE_KDF_KDFS)
return (SET_ERROR(EINVAL));
/*
* for raw receives we allow any number of pbkdf2iters since there
* won't be a chance for the user to change it.
@ -2271,6 +2304,7 @@ dsl_crypto_recv_raw_key_sync(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
uint64_t rddobj, one = 1;
uint8_t *keydata, *hmac_keydata, *iv, *mac;
uint64_t crypt, key_guid, keyformat, iters, salt;
uint64_t kdf = ZFS_PASSPHRASE_KDF_PBKDF2;
uint64_t version = ZIO_CRYPT_KEY_CURRENT_VERSION;
const char *keylocation = "prompt";
@ -2279,6 +2313,8 @@ dsl_crypto_recv_raw_key_sync(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
key_guid = fnvlist_lookup_uint64(nvl, DSL_CRYPTO_KEY_GUID);
keyformat = fnvlist_lookup_uint64(nvl,
zfs_prop_to_name(ZFS_PROP_KEYFORMAT));
(void) nvlist_lookup_uint64(nvl,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), &kdf);
iters = fnvlist_lookup_uint64(nvl,
zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS));
salt = fnvlist_lookup_uint64(nvl,
@ -2331,8 +2367,8 @@ dsl_crypto_recv_raw_key_sync(dsl_dataset_t *ds, nvlist_t *nvl, dmu_tx_t *tx)
/* sync the key data to the ZAP object on disk */
dsl_crypto_key_sync_impl(mos, dd->dd_crypto_obj, crypt,
rddobj, key_guid, iv, mac, keydata, hmac_keydata, keyformat, salt,
iters, tx);
rddobj, key_guid, iv, mac, keydata, hmac_keydata, keyformat, kdf,
salt, iters, tx);
}
static int
@ -2426,6 +2462,7 @@ dsl_crypto_populate_key_nvlist(objset_t *os, uint64_t from_ivset_guid,
dsl_pool_t *dp = ds->ds_dir->dd_pool;
objset_t *mos = dp->dp_meta_objset;
uint64_t crypt = 0, key_guid = 0, format = 0;
uint64_t kdf = ZFS_PASSPHRASE_KDF_PBKDF2;
uint64_t iters = 0, salt = 0, version = 0;
uint64_t to_ivset_guid = 0;
uint8_t raw_keydata[MASTER_KEY_MAX_LEN];
@ -2508,6 +2545,11 @@ dsl_crypto_populate_key_nvlist(objset_t *os, uint64_t from_ivset_guid,
if (ret != 0)
goto error_unlock;
ret = zap_lookup(dp->dp_meta_objset, rdd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), 8, 1, &kdf);
if (ret != 0 && ret != ENOENT)
goto error_unlock;
if (format == ZFS_KEYFORMAT_PASSPHRASE) {
ret = zap_lookup(dp->dp_meta_objset, rdd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), 8, 1, &iters);
@ -2537,6 +2579,7 @@ dsl_crypto_populate_key_nvlist(objset_t *os, uint64_t from_ivset_guid,
VERIFY0(nvlist_add_uint8_array(nvl, "portable_mac",
os->os_phys->os_portable_mac, ZIO_OBJSET_MAC_LEN));
fnvlist_add_uint64(nvl, zfs_prop_to_name(ZFS_PROP_KEYFORMAT), format);
fnvlist_add_uint64(nvl, zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), kdf);
fnvlist_add_uint64(nvl, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS), iters);
fnvlist_add_uint64(nvl, zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), salt);
fnvlist_add_uint64(nvl, "mdn_checksum", mdn->dn_checksum);
@ -2650,6 +2693,10 @@ dsl_dataset_crypt_stats(dsl_dataset_t *ds, nvlist_t *nv)
zfs_prop_to_name(ZFS_PROP_KEYFORMAT), 8, 1, &intval) == 0) {
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_KEYFORMAT, intval);
}
if (zap_lookup(dd->dd_pool->dp_meta_objset, dd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PASSPHRASE_KDF), 8, 1, &intval) == 0) {
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_PASSPHRASE_KDF, intval);
}
if (zap_lookup(dd->dd_pool->dp_meta_objset, dd->dd_crypto_obj,
zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT), 8, 1, &intval) == 0) {
dsl_prop_nvlist_add_uint64(nv, ZFS_PROP_PBKDF2_SALT, intval);

View File

@ -378,13 +378,16 @@ get_special_prop(lua_State *state, dsl_dataset_t *ds, const char *dsname,
}
case ZFS_PROP_KEYSTATUS:
case ZFS_PROP_KEYFORMAT: {
case ZFS_PROP_KEYFORMAT:
case ZFS_PROP_PASSPHRASE_KDF: {
/* provide defaults in case no crypto obj exists */
setpoint[0] = '\0';
if (zfs_prop == ZFS_PROP_KEYSTATUS)
numval = ZFS_KEYSTATUS_NONE;
else
else if (zfs_prop == ZFS_PROP_KEYFORMAT)
numval = ZFS_KEYFORMAT_NONE;
else
numval = ZFS_PASSPHRASE_KDF_PBKDF2;
nvlist_t *nvl, *propval;
nvl = fnvlist_alloc();

View File

@ -33,6 +33,8 @@
# 5. Unload the dataset's key
# 6. Attempt to load the dataset's key
#
# Do the same with Argon2id
#
verify_runnable "both"
@ -58,6 +60,7 @@ log_onexit cleanup
log_assert "'zfs change-key -o' should change the pbkdf2 iterations"
log_must eval "echo $PASSPHRASE > /$TESTPOOL/pkey"
log_must zfs create -o encryption=on -o keyformat=passphrase \
-o keylocation=file:///$TESTPOOL/pkey -o pbkdf2iters=200000 \
@ -72,4 +75,18 @@ log_must verify_pbkdf2iters $TESTPOOL/$TESTFS1 "150000"
log_must zfs unload-key $TESTPOOL/$TESTFS1
log_must zfs load-key $TESTPOOL/$TESTFS1
log_must zfs change-key -o passphrasekdf=argon2id $TESTPOOL/$TESTFS1
log_must verify_pbkdf2iters $TESTPOOL/$TESTFS1 "40000"
log_must zfs unload-key $TESTPOOL/$TESTFS1
log_must zfs load-key $TESTPOOL/$TESTFS1
log_must zfs change-key -o pbkdf2iters=10000 $TESTPOOL/$TESTFS1
log_must verify_pbkdf2iters $TESTPOOL/$TESTFS1 "10000"
log_must zfs unload-key $TESTPOOL/$TESTFS1
log_must zfs load-key $TESTPOOL/$TESTFS1
log_pass "'zfs change-key -o' changes the pbkdf2 iterations"