Merge 7c74565320
into 7ca7bb7fd7
This commit is contained in:
commit
34d4266afd
|
@ -28,6 +28,7 @@ libmount-dev
|
|||
libpam0g-dev
|
||||
libselinux1-dev
|
||||
libssl-dev
|
||||
libargon2-dev
|
||||
libtool
|
||||
libudev-dev
|
||||
linux-headers-generic
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, [])
|
||||
])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue