@@ -32,6 +32,19 @@
#define HKDF_HMAC_ALG "hmac(sha512)"
#define HKDF_HASHLEN SHA512_DIGEST_SIZE
+/*
+ * The list of contexts in which we use HKDF to derive additional keys from a
+ * master key. The values in this list are used as the first byte of the
+ * application-specific info string to guarantee that info strings are never
+ * repeated between contexts.
+ *
+ * Keys derived with different info strings are cryptographically isolated from
+ * each other --- knowledge of one derived key doesn't reveal any others.
+ * (This property is particularly important for the derived key used as the
+ * "key identifier", as that is stored in the clear.)
+ */
+#define HKDF_CONTEXT_KEY_IDENTIFIER 1
+
/*
* HKDF consists of two steps:
*
@@ -228,7 +241,12 @@ struct fscrypt_master_key {
*/
struct fscrypt_master_key_secret mk_secret;
- /* Arbitrary key descriptor which was assigned by userspace */
+ /*
+ * For v1 policy keys: an arbitrary key descriptor which was assigned by
+ * userspace (->descriptor).
+ *
+ * For v2 policy keys: a cryptographic hash of this key (->identifier).
+ */
struct fscrypt_key_specifier mk_spec;
/*
@@ -252,6 +270,8 @@ static inline int master_key_spec_len(const struct fscrypt_key_specifier *spec)
switch (spec->type) {
case FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR:
return FSCRYPT_KEY_DESCRIPTOR_SIZE;
+ case FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER:
+ return FSCRYPT_KEY_IDENTIFIER_SIZE;
}
return 0;
}
@@ -346,7 +366,7 @@ static struct key *search_fscrypt_keyring(struct key *keyring,
#define FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE \
(sizeof("fscrypt-") - 1 + sizeof(((struct super_block *)0)->s_id) + 1)
-#define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_DESCRIPTOR_SIZE + 1)
+#define FSCRYPT_MK_DESCRIPTION_SIZE (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + 1)
static void format_fs_keyring_description(
char description[FSCRYPT_FS_KEYRING_DESCRIPTION_SIZE],
@@ -550,6 +570,39 @@ int fscrypt_ioctl_add_key(struct file *filp, void __user *_uarg)
if (copy_from_user(secret.raw, uarg->raw, secret.size))
goto out_wipe_secret;
+ if (arg.key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
+ struct crypto_shash *hmac_tfm;
+
+ hmac_tfm = allocate_hmac_tfm(secret.raw, secret.size);
+ if (IS_ERR(hmac_tfm)) {
+ err = PTR_ERR(hmac_tfm);
+ goto out_wipe_secret;
+ }
+
+ /*
+ * Hash the master key to get the key identifier, then return it
+ * to userspace. Specifically, we derive the key identifier
+ * from the master key with HKDF-SHA512, using a unique
+ * application-specific info string. This is preferable to a
+ * simple truncated SHA512 because it means we only ever use the
+ * master key for a single cryptographic primitive (HKDF-SHA512)
+ * rather than two (HKDF-SHA512 and SHA512). It *maybe* would
+ * be okay, but cryptographically it would be bad practice.
+ */
+ err = hkdf_expand(hmac_tfm, HKDF_CONTEXT_KEY_IDENTIFIER,
+ NULL, 0, arg.key_spec.identifier,
+ FSCRYPT_KEY_IDENTIFIER_SIZE);
+ crypto_free_shash(hmac_tfm);
+ if (err)
+ goto out_wipe_secret;
+
+ err = -EFAULT;
+ if (copy_to_user(uarg->key_spec.identifier,
+ arg.key_spec.identifier,
+ FSCRYPT_KEY_IDENTIFIER_SIZE))
+ goto out_wipe_secret;
+ }
+
err = add_master_key(sb, &secret, &arg.key_spec);
out_wipe_secret:
wipe_master_key_secret(&secret);
@@ -872,6 +925,10 @@ static int derive_key_aes(const u8 *master_key,
* Search the current task's subscribed keyrings for a "logon" key with
* description prefix:descriptor, and if found acquire a read lock on it and
* return a pointer to its validated payload in *payload_ret.
+ *
+ * This is only used for v1 encryption policies, where keys are identified by
+ * master_key_descriptor. With newer policy versions, only the filesystem-level
+ * keyring (->s_master_keys) is supported.
*/
static struct key *
find_and_lock_process_key(const char *prefix,
@@ -977,17 +1034,23 @@ static int find_and_derive_key(const struct inode *inode,
memcpy(mk_spec.descriptor, ctx->v1.master_key_descriptor,
FSCRYPT_KEY_DESCRIPTOR_SIZE);
break;
+ case FSCRYPT_CONTEXT_V2:
+ mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
+ memcpy(mk_spec.identifier, ctx->v2.master_key_identifier,
+ FSCRYPT_KEY_IDENTIFIER_SIZE);
+ break;
default:
- return -EOPNOTSUPP;
+ return -EOPNOTSUPP; /* should have been checked earlier too */
}
key = find_master_key(inode->i_sb, &mk_spec);
if (IS_ERR(key)) {
- if (key != ERR_PTR(-ENOKEY))
+ if (key != ERR_PTR(-ENOKEY) ||
+ ctx->v1.version != FSCRYPT_CONTEXT_V1)
return PTR_ERR(key);
/*
- * As a legacy fallback, we search the current task's subscribed
- * keyrings in addition to ->s_master_keys.
+ * As a legacy fallback for v1 policies, we search the current
+ * task's subscribed keyrings in addition to ->s_master_keys.
*/
return find_and_derive_key_legacy(inode, &ctx->v1, derived_key,
derived_keysize);
@@ -82,10 +82,12 @@ struct fscrypt_get_policy_ex_args {
struct fscrypt_key_specifier {
__u32 type;
#define FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR 1
+#define FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER 2
__u32 reserved;
union {
__u8 max_specifier[32];
__u8 descriptor[FSCRYPT_KEY_DESCRIPTOR_SIZE];
+ __u8 identifier[FSCRYPT_KEY_IDENTIFIER_SIZE];
};
};