From patchwork Mon Feb 28 07:47:15 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762484 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5FFF1C433EF for ; Mon, 28 Feb 2022 07:48:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232100AbiB1Htd (ORCPT ); Mon, 28 Feb 2022 02:49:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32910 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229664AbiB1Htd (ORCPT ); Mon, 28 Feb 2022 02:49:33 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6D74A3D1D2; Sun, 27 Feb 2022 23:48:54 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id EF7A861043; Mon, 28 Feb 2022 07:48:53 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 36E95C340F2; Mon, 28 Feb 2022 07:48:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646034533; bh=84bv7nDGRm4EoyqglFFRf2Xya8YKAEQz0KiwKkw4xVM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=utMiDQsQ1zWE8d2K5XMsIcV0mXAyxA5HImWiMC75BpL4YOh5e99Lp2EFXqoSsbK/H b8iRLL6ndVks9aOSa6UOI5mpBckOv+luzUiCwMY/1Pj+MmHLS7LPERD+/34FUWp6Qk XncIkuWCa2G1bZzSmQL3ZPOQV++rO553HUKYShf9h98g0F4QJsDQga24kJgtC7vdbO xuWzqxyi411sycjzMrjRYhv1YRgPZhG55oEydFAKTMKMXD171fqiwaW9xg5v7ch0EJ W83k4d1edanaN7odfZcvb2lgLO/2gKLEf36KvFRRaLJfB9blRLmQC1oR4E0QZoPxHd yxFHx1SsEXQKg== From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Gaurav Kashyap Subject: [RFC PATCH 1/8] fscrypt-crypt-util: use an explicit --direct-key option Date: Sun, 27 Feb 2022 23:47:15 -0800 Message-Id: <20220228074722.77008-2-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228074722.77008-1-ebiggers@kernel.org> References: <20220228074722.77008-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers Make fscrypt-crypt-util use an option --direct-key to specify the use of the DIRECT_KEY method for key derivation and IV generation. Previously, this method was implicitly detected via --mode-num being given without either --iv-ino-lblk-64 or --iv-ino-lblk-32. The benefit of this change is that it makes the various options to fscrypt-crypt-util behave more consistently. --direct-key, --iv-ino-lblk-64, and --iv-ino-lblk-32 now all work similarly (they select a key derivation and IV generation method); likewise, --mode-num, --file-nonce, --inode-number, and --fs-uuid now all work similarly (they provide information that key derivation and IV generation may need). Signed-off-by: Eric Biggers --- common/encrypt | 10 ++++---- src/fscrypt-crypt-util.c | 52 ++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/common/encrypt b/common/encrypt index f90c4ef0..2cf02ca0 100644 --- a/common/encrypt +++ b/common/encrypt @@ -842,27 +842,25 @@ _verify_ciphertext_for_encryption_policy() set_encpolicy_args+=" -c $contents_mode_num" set_encpolicy_args+=" -n $filenames_mode_num" + crypt_util_contents_args+=" --mode-num=$contents_mode_num" + crypt_util_filename_args+=" --mode-num=$filenames_mode_num" if (( policy_version > 1 )); then set_encpolicy_args+=" -v 2" crypt_util_args+=" --kdf=HKDF-SHA512" if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then - crypt_util_args+=" --mode-num=$contents_mode_num" + crypt_util_args+=" --direct-key" elif (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 )); then crypt_util_args+=" --iv-ino-lblk-64" - crypt_util_contents_args+=" --mode-num=$contents_mode_num" - crypt_util_filename_args+=" --mode-num=$filenames_mode_num" elif (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 )); then crypt_util_args+=" --iv-ino-lblk-32" - crypt_util_contents_args+=" --mode-num=$contents_mode_num" - crypt_util_filename_args+=" --mode-num=$filenames_mode_num" fi else if (( policy_flags & ~FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then _fail "unsupported flags for v1 policy: $policy_flags" fi if (( policy_flags & FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then - crypt_util_args+=" --kdf=none" + crypt_util_args+=" --direct-key --kdf=none" else crypt_util_args+=" --kdf=AES-128-ECB" fi diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c index 03cc3c4a..022ff7bd 100644 --- a/src/fscrypt-crypt-util.c +++ b/src/fscrypt-crypt-util.c @@ -64,6 +64,8 @@ static void usage(FILE *fp) " --block-size=BLOCK_SIZE Encrypt each BLOCK_SIZE bytes independently.\n" " Default: 4096 bytes\n" " --decrypt Decrypt instead of encrypt\n" +" --direct-key Use the format where the IVs include the file\n" +" nonce and the same key is shared across files.\n" " --file-nonce=NONCE File's nonce as a 32-character hex string\n" " --fs-uuid=UUID The filesystem UUID as a 32-character hex string.\n" " Required for --iv-ino-lblk-32 and\n" @@ -76,11 +78,10 @@ static void usage(FILE *fp) " 32-bit variant.\n" " --iv-ino-lblk-64 Use the format where the IVs include the inode\n" " number and the same key is shared across files.\n" -" Requires --kdf=HKDF-SHA512, --fs-uuid,\n" -" --inode-number, and --mode-num.\n" " --kdf=KDF Key derivation function to use: AES-128-ECB,\n" " HKDF-SHA512, or none. Default: none\n" -" --mode-num=NUM Derive per-mode key using mode number NUM\n" +" --mode-num=NUM The encryption mode number. This may be required\n" +" for key derivation, depending on other options.\n" " --padding=PADDING If last block is partial, zero-pad it to next\n" " PADDING-byte boundary. Default: BLOCK_SIZE\n" , fp); @@ -1790,6 +1791,7 @@ struct key_and_iv_params { u8 mode_num; u8 file_nonce[FILE_NONCE_SIZE]; bool file_nonce_specified; + bool direct_key; bool iv_ino_lblk_64; bool iv_ino_lblk_32; u64 block_number; @@ -1835,7 +1837,7 @@ static void get_key_and_iv(const struct key_and_iv_params *params, u8 *real_key, size_t real_key_size, union fscrypt_iv *iv) { - bool file_nonce_in_iv = false; + int iv_methods = 0; struct aes_key aes_key; u8 info[8 + 1 + 1 + UUID_SIZE] = "fscrypt"; size_t infolen = 8; @@ -1848,11 +1850,22 @@ static void get_key_and_iv(const struct key_and_iv_params *params, /* Overridden later for iv_ino_lblk_{64,32} */ iv->block_number = cpu_to_le64(params->block_number); - if (params->iv_ino_lblk_64 || params->iv_ino_lblk_32) { + iv_methods += params->direct_key; + iv_methods += params->iv_ino_lblk_64; + iv_methods += params->iv_ino_lblk_32; + if (iv_methods > 1) + die("Conflicting IV methods specified"); + if (iv_methods != 0 && params->kdf == KDF_AES_128_ECB) + die("--kdf=AES-128-ECB is incompatible with IV method options"); + + if (params->direct_key) { + if (!params->file_nonce_specified) + die("--direct-key requires file nonce"); + if (params->kdf != KDF_NONE && params->mode_num == 0) + die("--direct-key with KDF requires --mode-num"); + } else if (params->iv_ino_lblk_64 || params->iv_ino_lblk_32) { const char *opt = params->iv_ino_lblk_64 ? "--iv-ino-lblk-64" : "--iv-ino-lblk-32"; - if (params->iv_ino_lblk_64 && params->iv_ino_lblk_32) - die("--iv-ino-lblk-64 and --iv-ino-lblk-32 are mutually exclusive"); if (params->kdf != KDF_HKDF_SHA512) die("%s requires --kdf=HKDF-SHA512", opt); if (!params->fs_uuid_specified) @@ -1869,16 +1882,11 @@ static void get_key_and_iv(const struct key_and_iv_params *params, switch (params->kdf) { case KDF_NONE: - if (params->mode_num != 0) - die("--mode-num isn't supported with --kdf=none"); memcpy(real_key, params->master_key, real_key_size); - file_nonce_in_iv = true; break; case KDF_AES_128_ECB: if (!params->file_nonce_specified) - die("--file-nonce is required with --kdf=AES-128-ECB"); - if (params->mode_num != 0) - die("--mode-num isn't supported with --kdf=AES-128-ECB"); + die("--kdf=AES-128-ECB requires --file-nonce"); STATIC_ASSERT(FILE_NONCE_SIZE == AES_128_KEY_SIZE); ASSERT(real_key_size % AES_BLOCK_SIZE == 0); aes_setkey(&aes_key, params->file_nonce, AES_128_KEY_SIZE); @@ -1887,7 +1895,10 @@ static void get_key_and_iv(const struct key_and_iv_params *params, &real_key[i]); break; case KDF_HKDF_SHA512: - if (params->iv_ino_lblk_64) { + if (params->direct_key) { + info[infolen++] = HKDF_CONTEXT_DIRECT_KEY; + info[infolen++] = params->mode_num; + } else if (params->iv_ino_lblk_64) { info[infolen++] = HKDF_CONTEXT_IV_INO_LBLK_64_KEY; info[infolen++] = params->mode_num; memcpy(&info[infolen], params->fs_uuid, UUID_SIZE); @@ -1903,17 +1914,13 @@ static void get_key_and_iv(const struct key_and_iv_params *params, cpu_to_le32(hash_inode_number(params) + params->block_number); iv->inode_number = 0; - } else if (params->mode_num != 0) { - info[infolen++] = HKDF_CONTEXT_DIRECT_KEY; - info[infolen++] = params->mode_num; - file_nonce_in_iv = true; } else if (params->file_nonce_specified) { info[infolen++] = HKDF_CONTEXT_PER_FILE_ENC_KEY; memcpy(&info[infolen], params->file_nonce, FILE_NONCE_SIZE); infolen += FILE_NONCE_SIZE; } else { - die("With --kdf=HKDF-SHA512, at least one of --file-nonce and --mode-num must be specified"); + die("--kdf=HKDF-SHA512 requires --file-nonce or --iv-ino-lblk-*"); } hkdf_sha512(params->master_key, params->master_key_size, NULL, 0, info, infolen, real_key, real_key_size); @@ -1922,7 +1929,7 @@ static void get_key_and_iv(const struct key_and_iv_params *params, ASSERT(0); } - if (file_nonce_in_iv && params->file_nonce_specified) + if (params->direct_key) memcpy(iv->nonce, params->file_nonce, FILE_NONCE_SIZE); } @@ -1930,6 +1937,7 @@ enum { OPT_BLOCK_NUMBER, OPT_BLOCK_SIZE, OPT_DECRYPT, + OPT_DIRECT_KEY, OPT_FILE_NONCE, OPT_FS_UUID, OPT_HELP, @@ -1945,6 +1953,7 @@ static const struct option longopts[] = { { "block-number", required_argument, NULL, OPT_BLOCK_NUMBER }, { "block-size", required_argument, NULL, OPT_BLOCK_SIZE }, { "decrypt", no_argument, NULL, OPT_DECRYPT }, + { "direct-key", no_argument, NULL, OPT_DIRECT_KEY }, { "file-nonce", required_argument, NULL, OPT_FILE_NONCE }, { "fs-uuid", required_argument, NULL, OPT_FS_UUID }, { "help", no_argument, NULL, OPT_HELP }, @@ -1999,6 +2008,9 @@ int main(int argc, char *argv[]) case OPT_DECRYPT: decrypting = true; break; + case OPT_DIRECT_KEY: + params.direct_key = true; + break; case OPT_FILE_NONCE: if (hex2bin(optarg, params.file_nonce, FILE_NONCE_SIZE) != FILE_NONCE_SIZE) From patchwork Mon Feb 28 07:47:16 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762485 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 82691C433FE for ; Mon, 28 Feb 2022 07:48:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233689AbiB1Hte (ORCPT ); Mon, 28 Feb 2022 02:49:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32912 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231293AbiB1Htd (ORCPT ); Mon, 28 Feb 2022 02:49:33 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9D96C3D1D3; Sun, 27 Feb 2022 23:48:54 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 3CF496100E; Mon, 28 Feb 2022 07:48:54 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7A82AC340E7; Mon, 28 Feb 2022 07:48:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646034533; bh=+C0ISWOcH2v1GhaoFWSDaSnTtBvVfHQlIZyiB7bk9Pw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BfwRas64/Q/Jrvbz1zTyd4YaQzhJp07U7IGmETC0CJ3URDokgbErAArEZx/1z/cAD pGchxlQB2/qbMSqcD7Cb8umB5Y/0pHLRGrcxWi7xv9kkeAII3Qwrcjs3fYOyQhZJxt iVcSrVgfqUx46tJwURWOk5nA88jO7otFMHRm7AYwN1D5suWl6dzfi9z3XO2mgU9BKT QY20c/OtlzqpA/zoRQnK2C0oYfpkgJ6bKpcXZnN11/8kh11CmtFp+hY9eTv/iXKExd apJ/H2k6QE+BVRbAnJ5kNoQaD+1LisvjXJ5eq5O8scQtXVUcjNi0Ew3xqoRDOZtq+r bmIzp31SK5kjw== From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Gaurav Kashyap Subject: [RFC PATCH 2/8] fscrypt-crypt-util: refactor get_key_and_iv() Date: Sun, 27 Feb 2022 23:47:16 -0800 Message-Id: <20220228074722.77008-3-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228074722.77008-1-ebiggers@kernel.org> References: <20220228074722.77008-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers Split get_key_and_iv() into two distinct parts: (1) deriving the key and (2) generating the IV. Also, check for the presence of needed options just before they are used rather than doing it all up-front. These changes should make this code much easier to understand. Signed-off-by: Eric Biggers --- src/fscrypt-crypt-util.c | 115 ++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 51 deletions(-) diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c index 022ff7bd..0ecf9272 100644 --- a/src/fscrypt-crypt-util.c +++ b/src/fscrypt-crypt-util.c @@ -1828,16 +1828,9 @@ static u32 hash_inode_number(const struct key_and_iv_params *params) return (u32)siphash_1u64(hash_key.words, params->inode_number); } -/* - * Get the key and starting IV with which the encryption will actually be done. - * If a KDF was specified, a subkey is derived from the master key and the mode - * number or file nonce. Otherwise, the master key is used directly. - */ -static void get_key_and_iv(const struct key_and_iv_params *params, - u8 *real_key, size_t real_key_size, - union fscrypt_iv *iv) +static void derive_real_key(const struct key_and_iv_params *params, + u8 *real_key, size_t real_key_size) { - int iv_methods = 0; struct aes_key aes_key; u8 info[8 + 1 + 1 + UUID_SIZE] = "fscrypt"; size_t infolen = 8; @@ -1845,41 +1838,6 @@ static void get_key_and_iv(const struct key_and_iv_params *params, ASSERT(real_key_size <= params->master_key_size); - memset(iv, 0, sizeof(*iv)); - - /* Overridden later for iv_ino_lblk_{64,32} */ - iv->block_number = cpu_to_le64(params->block_number); - - iv_methods += params->direct_key; - iv_methods += params->iv_ino_lblk_64; - iv_methods += params->iv_ino_lblk_32; - if (iv_methods > 1) - die("Conflicting IV methods specified"); - if (iv_methods != 0 && params->kdf == KDF_AES_128_ECB) - die("--kdf=AES-128-ECB is incompatible with IV method options"); - - if (params->direct_key) { - if (!params->file_nonce_specified) - die("--direct-key requires file nonce"); - if (params->kdf != KDF_NONE && params->mode_num == 0) - die("--direct-key with KDF requires --mode-num"); - } else if (params->iv_ino_lblk_64 || params->iv_ino_lblk_32) { - const char *opt = params->iv_ino_lblk_64 ? "--iv-ino-lblk-64" : - "--iv-ino-lblk-32"; - if (params->kdf != KDF_HKDF_SHA512) - die("%s requires --kdf=HKDF-SHA512", opt); - if (!params->fs_uuid_specified) - die("%s requires --fs-uuid", opt); - if (params->inode_number == 0) - die("%s requires --inode-number", opt); - if (params->mode_num == 0) - die("%s requires --mode-num", opt); - if (params->block_number > UINT32_MAX) - die("%s can't use --block-number > UINT32_MAX", opt); - if (params->inode_number > UINT32_MAX) - die("%s can't use --inode-number > UINT32_MAX", opt); - } - switch (params->kdf) { case KDF_NONE: memcpy(real_key, params->master_key, real_key_size); @@ -1896,24 +1854,28 @@ static void get_key_and_iv(const struct key_and_iv_params *params, break; case KDF_HKDF_SHA512: if (params->direct_key) { + if (params->mode_num == 0) + die("--direct-key requires --mode-num"); info[infolen++] = HKDF_CONTEXT_DIRECT_KEY; info[infolen++] = params->mode_num; } else if (params->iv_ino_lblk_64) { + if (params->mode_num == 0) + die("--iv-ino-lblk-64 requires --mode-num"); + if (!params->fs_uuid_specified) + die("--iv-ino-lblk-64 requires --fs-uuid"); info[infolen++] = HKDF_CONTEXT_IV_INO_LBLK_64_KEY; info[infolen++] = params->mode_num; memcpy(&info[infolen], params->fs_uuid, UUID_SIZE); infolen += UUID_SIZE; - iv->block_number32 = cpu_to_le32(params->block_number); - iv->inode_number = cpu_to_le32(params->inode_number); } else if (params->iv_ino_lblk_32) { + if (params->mode_num == 0) + die("--iv-ino-lblk-32 requires --mode-num"); + if (!params->fs_uuid_specified) + die("--iv-ino-lblk-32 requires --fs-uuid"); info[infolen++] = HKDF_CONTEXT_IV_INO_LBLK_32_KEY; info[infolen++] = params->mode_num; memcpy(&info[infolen], params->fs_uuid, UUID_SIZE); infolen += UUID_SIZE; - iv->block_number32 = - cpu_to_le32(hash_inode_number(params) + - params->block_number); - iv->inode_number = 0; } else if (params->file_nonce_specified) { info[infolen++] = HKDF_CONTEXT_PER_FILE_ENC_KEY; memcpy(&info[infolen], params->file_nonce, @@ -1928,9 +1890,60 @@ static void get_key_and_iv(const struct key_and_iv_params *params, default: ASSERT(0); } +} - if (params->direct_key) +static void generate_iv(const struct key_and_iv_params *params, + union fscrypt_iv *iv) +{ + memset(iv, 0, sizeof(*iv)); + if (params->direct_key) { + if (!params->file_nonce_specified) + die("--direct-key requires file nonce"); + iv->block_number = cpu_to_le64(params->block_number); memcpy(iv->nonce, params->file_nonce, FILE_NONCE_SIZE); + } else if (params->iv_ino_lblk_64) { + if (params->block_number > UINT32_MAX) + die("iv-ino-lblk-64 can't use --block-number > UINT32_MAX"); + if (params->inode_number == 0) + die("iv-ino-lblk-64 requires --inode-number"); + if (params->inode_number > UINT32_MAX) + die("iv-ino-lblk-64 can't use --inode-number > UINT32_MAX"); + iv->block_number32 = cpu_to_le32(params->block_number); + iv->inode_number = cpu_to_le32(params->inode_number); + } else if (params->iv_ino_lblk_32) { + if (params->block_number > UINT32_MAX) + die("iv-ino-lblk-32 can't use --block-number > UINT32_MAX"); + if (params->inode_number == 0) + die("iv-ino-lblk-32 requires --inode-number"); + iv->block_number32 = cpu_to_le32(hash_inode_number(params) + + params->block_number); + } else { + iv->block_number = cpu_to_le64(params->block_number); + } +} + +/* + * Get the key and starting IV with which the encryption will actually be done. + * If a KDF was specified, then a subkey is derived from the master key. + * Otherwise, the master key is used directly. + */ +static void get_key_and_iv(const struct key_and_iv_params *params, + u8 *real_key, size_t real_key_size, + union fscrypt_iv *iv) +{ + int iv_methods = 0; + + iv_methods += params->direct_key; + iv_methods += params->iv_ino_lblk_64; + iv_methods += params->iv_ino_lblk_32; + if (iv_methods > 1) + die("Conflicting IV methods specified"); + if (iv_methods != 0 && params->kdf == KDF_AES_128_ECB) + die("--kdf=AES-128-ECB is incompatible with IV method options"); + + derive_real_key(params, real_key, real_key_size); + + generate_iv(params, iv); } enum { From patchwork Mon Feb 28 07:47:17 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762490 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 413E8C433FE for ; Mon, 28 Feb 2022 07:49:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231867AbiB1Hth (ORCPT ); Mon, 28 Feb 2022 02:49:37 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33090 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233711AbiB1Htf (ORCPT ); Mon, 28 Feb 2022 02:49:35 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DAABC3D1CE; Sun, 27 Feb 2022 23:48:56 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 3D17FB80E58; Mon, 28 Feb 2022 07:48:55 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id BE3FEC340F3; Mon, 28 Feb 2022 07:48:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646034533; bh=SVSbmmZuBNWRYo5l3+eRPfTfeDjvLCb7XGjy8DkFlBA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BWXczMefXLjGBj96On3ikhUuVhRq/7D5npkeBWWyKAmR7I39iKcvi3RyFEUF3YPKm x84aTpWMqL9JAg3fPWwy1ox5/22kipHipaTWoWxFrRP+85b8gwngzw04L6lcBkG4ei +e1ukdrIOhG/Hb6YmwP0r8CZ0fqmSy14TdC+qrpaYQyHq8/mj5wfAG+nsa5B0QOmVz LflPEm9HoSQ9WDlIJYvUAqapKNHY4wHwUsiMTtQVP/4z8g/+Ls6bm6hnvBPu5qgCOJ 2TaWGz3Ph/T8xUIC3Y4JbzqtmlxsJnDP7hcEy59CbDfOr3hWRdCjeXExQ2Nar//MZB v+4BCpel/vIgg== From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Gaurav Kashyap Subject: [RFC PATCH 3/8] fscrypt-crypt-util: add support for dumping key identifier Date: Sun, 27 Feb 2022 23:47:17 -0800 Message-Id: <20220228074722.77008-4-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228074722.77008-1-ebiggers@kernel.org> References: <20220228074722.77008-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers Add an option to fscrypt-crypt-util to make it compute the key identifier for the given key. This will allow testing the correctness of the filesystem's key identifier computation. Signed-off-by: Eric Biggers --- src/fscrypt-crypt-util.c | 82 +++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c index 0ecf9272..876d9f5c 100644 --- a/src/fscrypt-crypt-util.c +++ b/src/fscrypt-crypt-util.c @@ -46,7 +46,7 @@ static void usage(FILE *fp) { fputs( -"Usage: " PROGRAM_NAME " [OPTION]... CIPHER MASTER_KEY\n" +"Usage: " PROGRAM_NAME " [OPTION]... [CIPHER | --dump-key-identifier] MASTER_KEY\n" "\n" "Utility for verifying fscrypt-encrypted data. This program encrypts\n" "(or decrypts) the data on stdin using the given CIPHER with the given\n" @@ -66,6 +66,8 @@ static void usage(FILE *fp) " --decrypt Decrypt instead of encrypt\n" " --direct-key Use the format where the IVs include the file\n" " nonce and the same key is shared across files.\n" +" --dump-key-identifier Instead of encrypting/decrypting data, just\n" +" compute and dump the key identifier.\n" " --file-nonce=NONCE File's nonce as a 32-character hex string\n" " --fs-uuid=UUID The filesystem UUID as a 32-character hex string.\n" " Required for --iv-ino-lblk-32 and\n" @@ -1946,11 +1948,31 @@ static void get_key_and_iv(const struct key_and_iv_params *params, generate_iv(params, iv); } +static void do_dump_key_identifier(const struct key_and_iv_params *params) +{ + u8 info[9] = "fscrypt"; + u8 key_identifier[16]; + int i; + + if (params->kdf != KDF_HKDF_SHA512) + die("--dump-key-identifier requires --kdf=HKDF-SHA512"); + + info[8] = HKDF_CONTEXT_KEY_IDENTIFIER; + + hkdf_sha512(params->master_key, params->master_key_size, + NULL, 0, info, sizeof(info), key_identifier, + sizeof(key_identifier)); + + for (i = 0; i < sizeof(key_identifier); i++) + printf("%02x", key_identifier[i]); +} + enum { OPT_BLOCK_NUMBER, OPT_BLOCK_SIZE, OPT_DECRYPT, OPT_DIRECT_KEY, + OPT_DUMP_KEY_IDENTIFIER, OPT_FILE_NONCE, OPT_FS_UUID, OPT_HELP, @@ -1967,6 +1989,7 @@ static const struct option longopts[] = { { "block-size", required_argument, NULL, OPT_BLOCK_SIZE }, { "decrypt", no_argument, NULL, OPT_DECRYPT }, { "direct-key", no_argument, NULL, OPT_DIRECT_KEY }, + { "dump-key-identifier", no_argument, NULL, OPT_DUMP_KEY_IDENTIFIER }, { "file-nonce", required_argument, NULL, OPT_FILE_NONCE }, { "fs-uuid", required_argument, NULL, OPT_FS_UUID }, { "help", no_argument, NULL, OPT_HELP }, @@ -1983,9 +2006,10 @@ int main(int argc, char *argv[]) { size_t block_size = 4096; bool decrypting = false; + bool dump_key_identifier = false; struct key_and_iv_params params; size_t padding = 0; - const struct fscrypt_cipher *cipher; + const struct fscrypt_cipher *cipher = NULL; u8 real_key[MAX_KEY_SIZE]; union fscrypt_iv iv; char *tmp; @@ -2024,6 +2048,9 @@ int main(int argc, char *argv[]) case OPT_DIRECT_KEY: params.direct_key = true; break; + case OPT_DUMP_KEY_IDENTIFIER: + dump_key_identifier = true; + break; case OPT_FILE_NONCE: if (hex2bin(optarg, params.file_nonce, FILE_NONCE_SIZE) != FILE_NONCE_SIZE) @@ -2071,29 +2098,40 @@ int main(int argc, char *argv[]) argc -= optind; argv += optind; - if (argc != 2) { - usage(stderr); - return 2; + if (dump_key_identifier) { + if (argc != 1) { + usage(stderr); + return 2; + } + } else { + if (argc != 2) { + usage(stderr); + return 2; + } + cipher = find_fscrypt_cipher(*argv); + if (cipher == NULL) + die("Unknown cipher: %s", *argv); + + if (block_size < cipher->min_input_size) + die("Block size of %zu bytes is too small for cipher %s", + block_size, cipher->name); + argv++; } - - cipher = find_fscrypt_cipher(argv[0]); - if (cipher == NULL) - die("Unknown cipher: %s", argv[0]); - - if (block_size < cipher->min_input_size) - die("Block size of %zu bytes is too small for cipher %s", - block_size, cipher->name); - - params.master_key_size = hex2bin(argv[1], params.master_key, + params.master_key_size = hex2bin(*argv, params.master_key, MAX_KEY_SIZE); if (params.master_key_size < 0) - die("Invalid master_key: %s", argv[1]); - if (params.master_key_size < cipher->keysize) - die("Master key is too short for cipher %s", cipher->name); + die("Invalid master_key: %s", *argv); - get_key_and_iv(¶ms, real_key, cipher->keysize, &iv); - - crypt_loop(cipher, real_key, &iv, decrypting, block_size, padding, - params.iv_ino_lblk_64 || params.iv_ino_lblk_32); + if (dump_key_identifier) { + do_dump_key_identifier(¶ms); + } else { + if (params.master_key_size < cipher->keysize) + die("Master key is too short for cipher %s", + cipher->name); + get_key_and_iv(¶ms, real_key, cipher->keysize, &iv); + crypt_loop(cipher, real_key, &iv, decrypting, block_size, + padding, + params.iv_ino_lblk_64 || params.iv_ino_lblk_32); + } return 0; } From patchwork Mon Feb 28 07:47:18 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762486 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 78FD9C4167B for ; Mon, 28 Feb 2022 07:48:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231293AbiB1Hte (ORCPT ); Mon, 28 Feb 2022 02:49:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32914 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231740AbiB1Htd (ORCPT ); Mon, 28 Feb 2022 02:49:33 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 65F083D1D5; Sun, 27 Feb 2022 23:48:55 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id BB54861055; Mon, 28 Feb 2022 07:48:54 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0D75DC36AE2; Mon, 28 Feb 2022 07:48:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646034534; bh=k4S5di8zkPHlytVqNzUKf65R4jUD1HKT508FSPuOsA8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=sE14u/qwCnfBlxCteiw3dZcSa9M8Vn3tOo8P7cdM8vAixPOyz519NTtPjYlzRVORZ HDZskbayZNOnbmUYLaTQXIuqyzbCPIMlW0rzsuE+9n9krbzx8zun02OnXEdr05EDgY rQqNyqlA5J2tF2TWde7zGmBq6yN020ZILXYEsxqtJjBDKT2Pyq5/n4cSgsbon8dAfp 0iM2fSFyPGdcs00zbWN9BQN5Fopnl5L2FAQTO+oWwDJFacnJSDq9yBij0HLtebpcSe 7M2nt5BqoakhMncuY4LiS63y0xLXKc8Mab7JwyGIcehMUA3SMqO0QFlPwaRFdMDWTC chW2hWOHqIsxw== From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Gaurav Kashyap Subject: [RFC PATCH 4/8] common/encrypt: log full ciphertext verification params Date: Sun, 27 Feb 2022 23:47:18 -0800 Message-Id: <20220228074722.77008-5-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228074722.77008-1-ebiggers@kernel.org> References: <20220228074722.77008-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers To help with debugging, log some additional information to $seqres.full. Signed-off-by: Eric Biggers --- common/encrypt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/common/encrypt b/common/encrypt index 2cf02ca0..cf402570 100644 --- a/common/encrypt +++ b/common/encrypt @@ -908,6 +908,17 @@ _verify_ciphertext_for_encryption_policy() echo -e "\tfilenames_encryption_mode: $filenames_encryption_mode" [ $# -ne 0 ] && echo -e "\toptions: $*" + cat >> $seqres.full < X-Patchwork-Id: 12762487 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 295B2C46467 for ; Mon, 28 Feb 2022 07:48:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231740AbiB1Htf (ORCPT ); Mon, 28 Feb 2022 02:49:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32950 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231854AbiB1Htd (ORCPT ); Mon, 28 Feb 2022 02:49:33 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 85B043D1DA; Sun, 27 Feb 2022 23:48:55 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 0CA1B6106E; Mon, 28 Feb 2022 07:48:55 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 52693C36AE3; Mon, 28 Feb 2022 07:48:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646034534; bh=ihLUvkE02m6lI4esqM8Hd125A3xtzXTuiFtrNib3XVc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Levyra1PYc86mC1p8NL+ce4caM5S6JoIRbOe2EhVXj4/vifBKMEYC9Sn3h5ivHlIW dVDOywO16lu3WEcT5sVtTJm38bqdCOA3ZS6ibDIARv6ST0aZh9O4CVixb+IP/aBuEJ F8FFQ+TsV0UHhxe1w9hVv78YteBHG8j68Xfwh6IWI8Hv52YOLlRjD+QQ26smI2YZ/D qI6l6Ib3MibUKdd1W67YyYfRy2h6nbYFu1Q+HhenBjjVQ0uK9/JMhFRDDJdJ/7KUpt x/OA1RyykYYynwpTW8NOtDWn1lZnlb0cSNRmAqj5P8eiDpWW6BlukavebM9hdlbmef QLl9GLskZVRYA== From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Gaurav Kashyap Subject: [RFC PATCH 5/8] common/encrypt: verify the key identifiers Date: Sun, 27 Feb 2022 23:47:19 -0800 Message-Id: <20220228074722.77008-6-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228074722.77008-1-ebiggers@kernel.org> References: <20220228074722.77008-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers As part of all the ciphertext verification tests, verify that the filesystem correctly computed the key identifier from the key the test generated. This uses fscrypt-crypt-util to compute the key identifier. Previously this was only being tested indirectly, via the tests that happen to use the hardcoded $TEST_RAW_KEY and $TEST_KEY_IDENTIFIER. The new check provides better coverage. Signed-off-by: Eric Biggers --- common/encrypt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/encrypt b/common/encrypt index cf402570..d8e2dba9 100644 --- a/common/encrypt +++ b/common/encrypt @@ -902,6 +902,18 @@ _verify_ciphertext_for_encryption_policy() fi local raw_key_hex=$(echo "$raw_key" | tr -d '\\x') + if (( policy_version > 1 )); then + echo "Verifying key identifier" >> $seqres.full + expected_identifier=$($here/src/fscrypt-crypt-util \ + --dump-key-identifier "$raw_key_hex" \ + $crypt_util_args) + if [ "$expected_identifier" != "$keyspec" ]; then + echo "KEY IDENTIFIER MISMATCH!" + echo " Expected: $expected_identifier" + echo " Actual: $keyspec" + fi + fi + echo echo -e "Verifying ciphertext with parameters:" echo -e "\tcontents_encryption_mode: $contents_encryption_mode" From patchwork Mon Feb 28 07:47:20 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762492 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8D2C7C4707F for ; Mon, 28 Feb 2022 07:49:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233731AbiB1Hti (ORCPT ); Mon, 28 Feb 2022 02:49:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33160 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233723AbiB1Htg (ORCPT ); Mon, 28 Feb 2022 02:49:36 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 997053D1D2; Sun, 27 Feb 2022 23:48:57 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 132BFB80E5C; Mon, 28 Feb 2022 07:48:56 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9604FC340F4; Mon, 28 Feb 2022 07:48:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646034534; bh=MsaVrw266YzU16CjaXzIUoUY0yLlOc2autQhKBnUZPY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MerbDE00Ntfj86QYmgD3T4DRWAJWGeFWSK7YKVp7h2omJChYuNyu7oXY5QX2bGc5K fTFWtI0Bjj8RPJfN7pjp36t9cUQBXir8NG3qHS3Zs06tXnPi1NZtj4fjzpa0lFp4W1 fcEtwQIyTek+FefTWh2gWuDYffwGFNNGfwpiRfYaRDCOMC5DIoNXQ4zuqOvD7Ez+ZD I/lIOfIJ0vS983oc9ugU7TYBZyFkYBSasvwm6WrJ12NN2osRCAgTcyBSqbyvaWgwuI 8FXm/Li8Zxbx0ya5oiwoz/VzqtfdRh8r1hHrr9dHtx7uicGffD/W4SilHIaE63XnwT F7INLtZ1rbRHg== From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Gaurav Kashyap Subject: [RFC PATCH 6/8] fscrypt-crypt-util: add hardware KDF support Date: Sun, 27 Feb 2022 23:47:20 -0800 Message-Id: <20220228074722.77008-7-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228074722.77008-1-ebiggers@kernel.org> References: <20220228074722.77008-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers Add support to fscrypt-crypt-util for replicating the extra KDF (Key Derivation Function) step that is required when a hardware-wrapped key is used. This step normally occurs in hardware, but we need to replicate it for testing purposes. Note, some care was needed to handle the fact that both inlinecrypt_key and sw_secret can be needed in a single run of fscrypt-crypt-util. Namely, with --iv-ino-lblk-32, inlinecrypt_key is needed for the en/decryption while sw_secret is needed for hash_inode_number(). Signed-off-by: Eric Biggers --- src/fscrypt-crypt-util.c | 253 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 242 insertions(+), 11 deletions(-) diff --git a/src/fscrypt-crypt-util.c b/src/fscrypt-crypt-util.c index 876d9f5c..7c19bb90 100644 --- a/src/fscrypt-crypt-util.c +++ b/src/fscrypt-crypt-util.c @@ -68,6 +68,10 @@ static void usage(FILE *fp) " nonce and the same key is shared across files.\n" " --dump-key-identifier Instead of encrypting/decrypting data, just\n" " compute and dump the key identifier.\n" +" --enable-hw-kdf Apply the hardware KDF (replicated in software)\n" +" to the key before using it. Use this to\n" +" replicate the en/decryption that is done when\n" +" the filesystem is given a hardware-wrapped key.\n" " --file-nonce=NONCE File's nonce as a 32-character hex string\n" " --fs-uuid=UUID The filesystem UUID as a 32-character hex string.\n" " Required for --iv-ino-lblk-32 and\n" @@ -86,6 +90,10 @@ static void usage(FILE *fp) " for key derivation, depending on other options.\n" " --padding=PADDING If last block is partial, zero-pad it to next\n" " PADDING-byte boundary. Default: BLOCK_SIZE\n" +" --use-inlinecrypt-key In combination with --enable-hw-kdf, this causes\n" +" the en/decryption to be done with the \"inline\n" +" encryption key\" rather than with a key derived\n" +" from the \"software secret\".\n" , fp); } @@ -295,6 +303,12 @@ typedef struct { __le64 hi; } ble128; +/* Same as ble128 but with big-endian byte order */ +typedef struct { + __be64 hi; + __be64 lo; +} bbe128; + /* Multiply a GF(2^128) element by the polynomial 'x' */ static inline void gf2_128_mul_x(ble128 *t) { @@ -305,6 +319,16 @@ static inline void gf2_128_mul_x(ble128 *t) t->lo = cpu_to_le64((lo << 1) ^ ((hi & (1ULL << 63)) ? 0x87 : 0)); } +/* Same as gf2_128_mul_x() but with big-endian byte order */ +static inline void gf2_128_mul_x_be(bbe128 *t) +{ + u64 lo = be64_to_cpu(t->lo); + u64 hi = be64_to_cpu(t->hi); + + t->hi = cpu_to_be64((hi << 1) | (lo >> 63)); + t->lo = cpu_to_be64((lo << 1) ^ ((hi & (1ULL << 63)) ? 0x87 : 0)); +} + /*----------------------------------------------------------------------------* * Group arithmetic * *----------------------------------------------------------------------------*/ @@ -1176,6 +1200,67 @@ static void aes_128_cts_cbc_decrypt(const u8 key[AES_128_KEY_SIZE], aes_cts_cbc_decrypt(key, AES_128_KEY_SIZE, iv, src, dst, nbytes); } +static void aes_256_cmac(const u8 key[AES_256_KEY_SIZE], + const u8 *msg, size_t msglen, u8 mac[AES_BLOCK_SIZE]) +{ + const size_t partial = msglen % AES_BLOCK_SIZE; + const size_t full_blocks = msglen ? (msglen - 1) / AES_BLOCK_SIZE : 0; + struct aes_key k; + bbe128 subkey; + size_t i; + + aes_setkey(&k, key, AES_256_KEY_SIZE); + memset(&subkey, 0, sizeof(subkey)); + aes_encrypt(&k, (u8 *)&subkey, (u8 *)&subkey); + gf2_128_mul_x_be(&subkey); + + memset(mac, 0, AES_BLOCK_SIZE); + for (i = 0; i < full_blocks * AES_BLOCK_SIZE; i += AES_BLOCK_SIZE) { + xor(mac, mac, &msg[i], AES_BLOCK_SIZE); + aes_encrypt(&k, mac, mac); + } + xor(mac, mac, &msg[i], msglen - i); + if (partial != 0 || msglen == 0) { + mac[msglen - i] ^= 0x80; + gf2_128_mul_x_be(&subkey); + } + xor(mac, mac, (u8 *)&subkey, AES_BLOCK_SIZE); + aes_encrypt(&k, mac, mac); +} + +#ifdef ENABLE_ALG_TESTS +#include +static void test_aes_256_cmac(void) +{ + unsigned long num_tests = NUM_ALG_TEST_ITERATIONS; + CMAC_CTX *ctx = CMAC_CTX_new(); + + ASSERT(ctx != NULL); + while (num_tests--) { + u8 key[AES_256_KEY_SIZE]; + u8 msg[128]; + u8 mac[AES_BLOCK_SIZE]; + u8 ref_mac[sizeof(mac)]; + const size_t msglen = 1 + (rand() % sizeof(msg)); + size_t out_len = 0; + + rand_bytes(key, sizeof(key)); + rand_bytes(msg, msglen); + + aes_256_cmac(key, msg, msglen, mac); + + ASSERT(ctx != NULL); + ASSERT(CMAC_Init(ctx, key, sizeof(key), EVP_aes_256_cbc(), + NULL) == 1); + ASSERT(CMAC_Update(ctx, msg, msglen) == 1); + ASSERT(CMAC_Final(ctx, ref_mac, &out_len)); + ASSERT(out_len == sizeof(mac)); + ASSERT(memcmp(mac, ref_mac, sizeof(mac)) == 0); + } + CMAC_CTX_free(ctx); +} +#endif /* ENABLE_ALG_TESTS */ + /*----------------------------------------------------------------------------* * XChaCha12 stream cipher * *----------------------------------------------------------------------------*/ @@ -1787,9 +1872,17 @@ static u8 parse_mode_number(const char *arg) } struct key_and_iv_params { + /* + * If enable_hw_kdf=true, then master_key and sw_secret will differ. + * Otherwise they will be the same. + */ u8 master_key[MAX_KEY_SIZE]; int master_key_size; + u8 sw_secret[MAX_KEY_SIZE]; + int sw_secret_size; enum kdf_algorithm kdf; + bool enable_hw_kdf; + bool use_inlinecrypt_key; u8 mode_num; u8 file_nonce[FILE_NONCE_SIZE]; bool file_nonce_specified; @@ -1802,13 +1895,14 @@ struct key_and_iv_params { bool fs_uuid_specified; }; -#define HKDF_CONTEXT_KEY_IDENTIFIER 1 +#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_STANDARD_KEY 1 #define HKDF_CONTEXT_PER_FILE_ENC_KEY 2 #define HKDF_CONTEXT_DIRECT_KEY 3 #define HKDF_CONTEXT_IV_INO_LBLK_64_KEY 4 #define HKDF_CONTEXT_DIRHASH_KEY 5 #define HKDF_CONTEXT_IV_INO_LBLK_32_KEY 6 #define HKDF_CONTEXT_INODE_HASH_KEY 7 +#define HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY 8 /* Hash the file's inode number using SipHash keyed by a derived key */ static u32 hash_inode_number(const struct key_and_iv_params *params) @@ -1820,7 +1914,7 @@ static u32 hash_inode_number(const struct key_and_iv_params *params) } hash_key; info[8] = HKDF_CONTEXT_INODE_HASH_KEY; - hkdf_sha512(params->master_key, params->master_key_size, + hkdf_sha512(params->sw_secret, params->sw_secret_size, NULL, 0, info, sizeof(info), hash_key.bytes, sizeof(hash_key)); @@ -1830,6 +1924,102 @@ static u32 hash_inode_number(const struct key_and_iv_params *params) return (u32)siphash_1u64(hash_key.words, params->inode_number); } +/* + * Replicate the hardware KDF, given the raw master key and the context + * indicating which type of key to derive. + * + * Detailed explanation: + * + * With hardware-wrapped keys, an extra level is inserted into fscrypt's key + * hierarchy, above what was previously the root: + * + * master_key + * | + * ------------- + * | | + * | | + * inlinecrypt_key sw_secret + * | + * | + * (everything else) + * + * From the master key, the "inline encryption key" (inlinecrypt_key) and + * "software secret" (sw_secret) are derived. The inlinecrypt_key is used to + * encrypt file contents. The sw_secret is used just like the old master key, + * except that it isn't used to derive the file contents key. (I.e., it's used + * to derive filenames encryption keys, key identifiers, inode hash keys, etc.) + * + * Normally, software only sees master_key in "wrapped" form, and can never see + * inlinecrypt_key at all. Only specialized hardware can access the raw + * master_key to derive the subkeys, in a step we call the "HW KDF". + * + * However, the HW KDF is a well-specified algorithm, and when a + * hardware-wrapped key is initially created, software can choose to import a + * raw key. This allows software to test the feature by replicating the HW KDF. + * + * This is what this function does; it will derive either the inlinecrypt_key + * key or the sw_secret, depending on the KDF context passed. + */ +static void hw_kdf(const u8 *master_key, size_t master_key_size, + const u8 *ctx, size_t ctx_size, + u8 *derived_key, size_t derived_key_size) +{ + static const u8 label[11] = "\0\0\x40\0\0\0\0\0\0\0\x20"; + u8 info[128]; + size_t i; + + if (master_key_size != AES_256_KEY_SIZE) + die("--hw-kdf requires a 32-byte master key"); + ASSERT(derived_key_size % AES_BLOCK_SIZE == 0); + + /* + * This is NIST SP 800-108 "KDF in Counter Mode" with AES-256-CMAC as + * the PRF and a particular choice of labels and contexts. + */ + for (i = 0; i < derived_key_size; i += AES_BLOCK_SIZE) { + u8 *p = info; + + ASSERT(sizeof(__be32) + sizeof(label) + 1 + ctx_size + + sizeof(__be32) <= sizeof(info)); + + put_unaligned_be32(1 + (i / AES_BLOCK_SIZE), p); + p += sizeof(__be32); + memcpy(p, label, sizeof(label)); + p += sizeof(label); + *p++ = 0; + memcpy(p, ctx, ctx_size); + p += ctx_size; + put_unaligned_be32(derived_key_size * 8, p); + p += sizeof(__be32); + + aes_256_cmac(master_key, info, p - info, &derived_key[i]); + } +} + +#define INLINECRYPT_KEY_SIZE 64 +#define SW_SECRET_SIZE 32 + +static void derive_inline_encryption_key(const u8 *master_key, + size_t master_key_size, + u8 inlinecrypt_key[INLINECRYPT_KEY_SIZE]) +{ + static const u8 ctx[36] = + "inline encryption key\0\0\0\0\0\0\x03\x43\0\x82\x50\0\0\0\0"; + + hw_kdf(master_key, master_key_size, ctx, sizeof(ctx), + inlinecrypt_key, INLINECRYPT_KEY_SIZE); +} + +static void derive_sw_secret(const u8 *master_key, size_t master_key_size, + u8 sw_secret[SW_SECRET_SIZE]) +{ + static const u8 ctx[28] = + "raw secret\0\0\0\0\0\0\0\0\0\x03\x17\0\x80\x50\0\0\0\0"; + + hw_kdf(master_key, master_key_size, ctx, sizeof(ctx), + sw_secret, SW_SECRET_SIZE); +} + static void derive_real_key(const struct key_and_iv_params *params, u8 *real_key, size_t real_key_size) { @@ -1838,11 +2028,30 @@ static void derive_real_key(const struct key_and_iv_params *params, size_t infolen = 8; size_t i; - ASSERT(real_key_size <= params->master_key_size); + if (params->use_inlinecrypt_key) { + /* + * With --use-inlinecrypt-key, we need to use the "hardware KDF" + * rather than the normal fscrypt KDF. Note that the fscrypt + * KDF might still be used elsewhere, e.g. hash_inode_number() + * -- it just won't be used for the actual encryption key. + */ + if (!params->enable_hw_kdf) + die("--use-inlinecrypt-key requires --enable-hw-kdf"); + if (!params->iv_ino_lblk_64 && !params->iv_ino_lblk_32) + die("--use-inlinecrypt-key requires one of --iv-ino-lblk-*"); + if (real_key_size != INLINECRYPT_KEY_SIZE) + die("cipher not compatible with --use-inlinecrypt-key"); + derive_inline_encryption_key(params->master_key, + params->master_key_size, real_key); + return; + } + + if (params->sw_secret_size < real_key_size) + die("Master key is too short for cipher"); switch (params->kdf) { case KDF_NONE: - memcpy(real_key, params->master_key, real_key_size); + memcpy(real_key, params->sw_secret, real_key_size); break; case KDF_AES_128_ECB: if (!params->file_nonce_specified) @@ -1851,7 +2060,7 @@ static void derive_real_key(const struct key_and_iv_params *params, ASSERT(real_key_size % AES_BLOCK_SIZE == 0); aes_setkey(&aes_key, params->file_nonce, AES_128_KEY_SIZE); for (i = 0; i < real_key_size; i += AES_BLOCK_SIZE) - aes_encrypt(&aes_key, ¶ms->master_key[i], + aes_encrypt(&aes_key, ¶ms->sw_secret[i], &real_key[i]); break; case KDF_HKDF_SHA512: @@ -1886,7 +2095,7 @@ static void derive_real_key(const struct key_and_iv_params *params, } else { die("--kdf=HKDF-SHA512 requires --file-nonce or --iv-ino-lblk-*"); } - hkdf_sha512(params->master_key, params->master_key_size, + hkdf_sha512(params->sw_secret, params->sw_secret_size, NULL, 0, info, infolen, real_key, real_key_size); break; default: @@ -1957,9 +2166,12 @@ static void do_dump_key_identifier(const struct key_and_iv_params *params) if (params->kdf != KDF_HKDF_SHA512) die("--dump-key-identifier requires --kdf=HKDF-SHA512"); - info[8] = HKDF_CONTEXT_KEY_IDENTIFIER; + if (params->enable_hw_kdf) + info[8] = HKDF_CONTEXT_KEY_IDENTIFIER_FOR_HW_WRAPPED_KEY; + else + info[8] = HKDF_CONTEXT_KEY_IDENTIFIER_FOR_STANDARD_KEY; - hkdf_sha512(params->master_key, params->master_key_size, + hkdf_sha512(params->sw_secret, params->sw_secret_size, NULL, 0, info, sizeof(info), key_identifier, sizeof(key_identifier)); @@ -1973,6 +2185,7 @@ enum { OPT_DECRYPT, OPT_DIRECT_KEY, OPT_DUMP_KEY_IDENTIFIER, + OPT_ENABLE_HW_KDF, OPT_FILE_NONCE, OPT_FS_UUID, OPT_HELP, @@ -1982,6 +2195,7 @@ enum { OPT_KDF, OPT_MODE_NUM, OPT_PADDING, + OPT_USE_INLINECRYPT_KEY, }; static const struct option longopts[] = { @@ -1990,6 +2204,7 @@ static const struct option longopts[] = { { "decrypt", no_argument, NULL, OPT_DECRYPT }, { "direct-key", no_argument, NULL, OPT_DIRECT_KEY }, { "dump-key-identifier", no_argument, NULL, OPT_DUMP_KEY_IDENTIFIER }, + { "enable-hw-kdf", no_argument, NULL, OPT_ENABLE_HW_KDF }, { "file-nonce", required_argument, NULL, OPT_FILE_NONCE }, { "fs-uuid", required_argument, NULL, OPT_FS_UUID }, { "help", no_argument, NULL, OPT_HELP }, @@ -1999,6 +2214,7 @@ static const struct option longopts[] = { { "kdf", required_argument, NULL, OPT_KDF }, { "mode-num", required_argument, NULL, OPT_MODE_NUM }, { "padding", required_argument, NULL, OPT_PADDING }, + { "use-inlinecrypt-key", no_argument, NULL, OPT_USE_INLINECRYPT_KEY }, { NULL, 0, NULL, 0 }, }; @@ -2025,6 +2241,7 @@ int main(int argc, char *argv[]) test_hkdf_sha512(); test_aes_256_xts(); test_aes_256_cts_cbc(); + test_aes_256_cmac(); test_adiantum(); #endif @@ -2051,6 +2268,9 @@ int main(int argc, char *argv[]) case OPT_DUMP_KEY_IDENTIFIER: dump_key_identifier = true; break; + case OPT_ENABLE_HW_KDF: + params.enable_hw_kdf = true; + break; case OPT_FILE_NONCE: if (hex2bin(optarg, params.file_nonce, FILE_NONCE_SIZE) != FILE_NONCE_SIZE) @@ -2090,6 +2310,9 @@ int main(int argc, char *argv[]) padding > INT_MAX) die("Invalid padding amount: %s", optarg); break; + case OPT_USE_INLINECRYPT_KEY: + params.use_inlinecrypt_key = true; + break; default: usage(stderr); return 2; @@ -2122,12 +2345,20 @@ int main(int argc, char *argv[]) if (params.master_key_size < 0) die("Invalid master_key: %s", *argv); + /* Derive sw_secret from master_key, if needed. */ + if (params.enable_hw_kdf) { + derive_sw_secret(params.master_key, params.master_key_size, + params.sw_secret); + params.sw_secret_size = SW_SECRET_SIZE; + } else { + memcpy(params.sw_secret, params.master_key, + params.master_key_size); + params.sw_secret_size = params.master_key_size; + } + if (dump_key_identifier) { do_dump_key_identifier(¶ms); } else { - if (params.master_key_size < cipher->keysize) - die("Master key is too short for cipher %s", - cipher->name); get_key_and_iv(¶ms, real_key, cipher->keysize, &iv); crypt_loop(cipher, real_key, &iv, decrypting, block_size, padding, From patchwork Mon Feb 28 07:47:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762489 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id BAC03C433F5 for ; Mon, 28 Feb 2022 07:48:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233715AbiB1Htf (ORCPT ); Mon, 28 Feb 2022 02:49:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32976 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233687AbiB1Hte (ORCPT ); Mon, 28 Feb 2022 02:49:34 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 924263D1EE; Sun, 27 Feb 2022 23:48:55 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 2EC12610A1; Mon, 28 Feb 2022 07:48:55 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D994CC340F5; Mon, 28 Feb 2022 07:48:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646034535; bh=rqA/l3I5m4OBXf1eHf/RwStmBflVY6DtAmuwtrEw/5c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=prgicrLAfOZTl8nCZnaRWFpJseTYxq/jQdII8MIynAeCRB5TVtnWM5AKvWUgBPrvI JzMuprzehMwDDeHvtdi0JU/l5Mdxc5vFHnIs9rv0LCnNYZ4uylvqamFB8lYZe59uCX 4URD5bfAqxlOHC2ENciizA2ly1tZh3GETTS/AAaYKakvTftM4Zf9MLwZFu6lR79JG4 TpC1OMEObBUdAWIeQEkVeIpDM+9X6ommJBGHrV5kb92icjGgEQhkUJItthOieBloXF HIMgFRDATFF+IrbKcYn2AtLOk7gwl3qxS/96B/cSHlaMM4w4K+vcI9yArLLu9Kw9Wk 05TOcjQ1C4fXg== From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Gaurav Kashyap Subject: [RFC PATCH 7/8] common/encrypt: support hardware-wrapped key testing Date: Sun, 27 Feb 2022 23:47:21 -0800 Message-Id: <20220228074722.77008-8-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228074722.77008-1-ebiggers@kernel.org> References: <20220228074722.77008-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers To support testing the kernel's support for hardware-wrapped inline encryption keys, add some new functions to common/encrypt: - _require_hw_wrapped_key_support(): Checks that a block device supports hardware-wrapped keys and that some related fscryptctl commands are present. - _require_scratch_inlinecrypt(): Checks that the filesystem accepts the inlinecrypt mount option. - _generate_raw_hw_key(): Generates a raw key of an appropriate size for importing as a hardware-wrapped key. - _add_hw_wrapped_key(): Imports and prepares a hardware-wrapped key, then adds it to a filesystem. In addition, update _require_encryption_policy_support() and _verify_ciphertext_for_encryption_policy() to support FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY. Note: some of this relies on being able to call new block device ioctls. For now these are accessed through the fscryptctl program. Signed-off-by: Eric Biggers --- common/config | 1 + common/encrypt | 116 ++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 11 deletions(-) diff --git a/common/config b/common/config index 479e50d1..85a7919c 100644 --- a/common/config +++ b/common/config @@ -228,6 +228,7 @@ export E2IMAGE_PROG="$(type -P e2image)" export BLKZONE_PROG="$(type -P blkzone)" export GZIP_PROG="$(type -P gzip)" export BTRFS_IMAGE_PROG="$(type -P btrfs-image)" +export FSCRYPTCTL_PROG="$(type -P fscryptctl)" # use 'udevadm settle' or 'udevsettle' to wait for lv to be settled. # newer systems have udevadm command but older systems like RHEL5 don't. diff --git a/common/encrypt b/common/encrypt index d8e2dba9..e91e05c4 100644 --- a/common/encrypt +++ b/common/encrypt @@ -58,22 +58,21 @@ _require_scratch_encryption() # If required, check for support for the specific type of encryption # policy required by the test. if [ $# -ne 0 ]; then - _require_encryption_policy_support $SCRATCH_MNT "$@" + _require_scratch_encryption_policy_support "$@" fi _scratch_unmount } -_require_encryption_policy_support() +_require_scratch_encryption_policy_support() { - local mnt=$1 - local dir=$mnt/tmpdir + local dir=$SCRATCH_MNT/tmpdir local set_encpolicy_args="" local policy_flags=0 local policy_version=1 local c - OPTIND=2 + OPTIND=1 while getopts "c:n:f:v:" c; do case $c in c|n) @@ -94,6 +93,12 @@ _require_encryption_policy_support() done set_encpolicy_args=${set_encpolicy_args# } + if (( policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )); then + echo "Checking whether $SCRATCH_DEV supports hardware-wrapped keys" \ + >> $seqres.full + _require_hw_wrapped_key_support $SCRATCH_DEV + fi + echo "Checking whether kernel supports encryption policy: $set_encpolicy_args" \ >> $seqres.full @@ -117,8 +122,15 @@ _require_encryption_policy_support() # Both the kernel and xfs_io support v2 encryption policies, and # therefore also filesystem-level keys -- since that's the only # way to provide keys for v2 policies. - local raw_key=$(_generate_raw_encryption_key) - local keyspec=$(_add_enckey $mnt "$raw_key" | awk '{print $NF}') + if (( policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )); then + local raw_key=$(_generate_raw_hw_key) + local keyspec=$(_add_hw_wrapped_key $SCRATCH_DEV \ + $SCRATCH_MNT "$raw_key") + else + local raw_key=$(_generate_raw_encryption_key) + local keyspec=$(_add_enckey $SCRATCH_MNT "$raw_key" | \ + awk '{print $NF}') + fi else _require_command "$KEYCTL_PROG" keyctl _new_session_keyring @@ -143,6 +155,47 @@ _require_encryption_policy_support() rm -r $dir } +# Require that the filesystem accepts the "inlinecrypt" mount option. +# +# Note: this doesn't check whether $SCRATCH_DEV has any specific inline +# encryption capabilities. For encryption policies that require specific +# capabilities, support is detected later by +# _require_scratch_encryption_policy_support() (provided that inlinecrypt has +# been added to $MOUNT_OPTIONS by that point). +_require_scratch_inlinecrypt() +{ + _require_scratch + _scratch_mkfs &>> $seqres.full + if ! _try_scratch_mount -o inlinecrypt &>> $seqres.full; then + _notrun "filesystem doesn't support -o inlinecrypt" + fi +} + +# Require that the given block device supports hardware-wrapped inline +# encryption keys, and require that a command-line tool that supports +# importing/generating/preparing them is available. +_require_hw_wrapped_key_support() +{ + local dev=$1 + + _require_command "$FSCRYPTCTL_PROG" fscryptctl + if ! "$FSCRYPTCTL_PROG" --help | grep -q "import_hw_wrapped_key"; then + _notrun "fscryptctl too old; doesn't support hardware-wrapped inline encryption keys" + fi + + if ! head -c $RAW_HW_KEY_SIZE /dev/urandom | \ + "$FSCRYPTCTL_PROG" import_hw_wrapped_key "$dev" \ + >/dev/null 2>$tmp.err + then + if grep -E -q \ + "(Operation not supported)|(Inappropriate ioctl for device)" $tmp.err + then + _notrun "$dev doesn't support hardware-wrapped inline encryption keys" + fi + _fail "Unexpected error from fscryptctl import_hw_wrapped_key: $(< $tmp.err)" + fi +} + _scratch_mkfs_encrypted() { case $FSTYP in @@ -220,14 +273,24 @@ _generate_key_descriptor() # Generate a raw encryption key, but don't add it to any keyring yet. _generate_raw_encryption_key() { + local size=${1:-64} local raw="" local i - for ((i = 0; i < 64; i++)); do + for ((i = 0; i < $size; i++)); do raw="${raw}\\x$(printf "%02x" $(( $RANDOM % 256 )))" done echo $raw } +RAW_HW_KEY_SIZE=32 + +# Generate a raw key of the proper size to be imported as a hardware-wrapped +# key. +_generate_raw_hw_key() +{ + _generate_raw_encryption_key $RAW_HW_KEY_SIZE +} + # Serialize an integer into a CPU-endian bytestring of the given length, and # print it as a string where each byte is hex-escaped. For example, # `_num_to_hex 1000 4` == "\xe8\x03\x00\x00" if the CPU is little endian. @@ -358,6 +421,21 @@ _add_enckey() echo -ne "$raw_key" | $XFS_IO_PROG -c "add_enckey $*" "$mnt" } +# Create a hardware-wrapped key from the given raw key using the given block +# device, add it to the given filesystem, and print the resulting key +# identifier. +_add_hw_wrapped_key() +{ + local dev=$1 + local mnt=$2 + local raw_key=$3 + + echo -ne "$raw_key" | \ + $FSCRYPTCTL_PROG import_hw_wrapped_key "$dev" | \ + $FSCRYPTCTL_PROG prepare_hw_wrapped_key "$dev" | \ + $FSCRYPTCTL_PROG add_key --hw-wrapped-key "$mnt" +} + _user_do_add_enckey() { local mnt=$1 @@ -771,6 +849,7 @@ FSCRYPT_MODE_ADIANTUM=9 FSCRYPT_POLICY_FLAG_DIRECT_KEY=0x04 FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64=0x08 FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32=0x10 +FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY=0x20 FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR=1 FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER=2 @@ -800,6 +879,7 @@ _fscrypt_mode_name_to_num() # 'direct': test the DIRECT_KEY policy flag # 'iv_ino_lblk_64': test the IV_INO_LBLK_64 policy flag # 'iv_ino_lblk_32': test the IV_INO_LBLK_32 policy flag +# 'hw_wrapped_key': test the HW_WRAPPED_KEY policy flag # _verify_ciphertext_for_encryption_policy() { @@ -832,6 +912,9 @@ _verify_ciphertext_for_encryption_policy() iv_ino_lblk_32) (( policy_flags |= FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 )) ;; + hw_wrapped_key) + (( policy_flags |= FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )) + ;; *) _fail "Unknown option '$opt' passed to ${FUNCNAME[0]}" ;; @@ -855,6 +938,10 @@ _verify_ciphertext_for_encryption_policy() elif (( policy_flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32 )); then crypt_util_args+=" --iv-ino-lblk-32" fi + if (( policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )); then + crypt_util_args+=" --enable-hw-kdf" + crypt_util_contents_args+=" --use-inlinecrypt-key" + fi else if (( policy_flags & ~FSCRYPT_POLICY_FLAG_DIRECT_KEY )); then _fail "unsupported flags for v1 policy: $policy_flags" @@ -891,11 +978,18 @@ _verify_ciphertext_for_encryption_policy() crypt_util_filename_args+="$crypt_util_args" echo "Generating encryption key" >> $seqres.full - local raw_key=$(_generate_raw_encryption_key) if (( policy_version > 1 )); then - local keyspec=$(_add_enckey $SCRATCH_MNT "$raw_key" \ - | awk '{print $NF}') + if (( policy_flags & FSCRYPT_POLICY_FLAG_HW_WRAPPED_KEY )); then + local raw_key=$(_generate_raw_hw_key) + local keyspec=$(_add_hw_wrapped_key $SCRATCH_DEV \ + $SCRATCH_MNT "$raw_key") + else + local raw_key=$(_generate_raw_encryption_key) + local keyspec=$(_add_enckey $SCRATCH_MNT "$raw_key" | \ + awk '{print $NF}') + fi else + local raw_key=$(_generate_raw_encryption_key) local keyspec=$(_generate_key_descriptor) _new_session_keyring _add_session_encryption_key $keyspec $raw_key From patchwork Mon Feb 28 07:47:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 12762488 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A8F88C35274 for ; Mon, 28 Feb 2022 07:49:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233721AbiB1Htg (ORCPT ); Mon, 28 Feb 2022 02:49:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:32978 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229664AbiB1Hte (ORCPT ); Mon, 28 Feb 2022 02:49:34 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 39DE23D1D2; Sun, 27 Feb 2022 23:48:56 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 72162610A4; Mon, 28 Feb 2022 07:48:55 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2854CC36AE3; Mon, 28 Feb 2022 07:48:55 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1646034535; bh=dNhFGMROSIDvPCnQSBawc2ub7nxSShPfueI2bzsEfMg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CFLzk888PsDt/RXZezLtV6WOwbd+ubyx26G96Nu2AmrslHMmvif2czdBzcFVZgoBC +rudEBCrT0p7dORa3edlu7/mxBhGQIPI35oBF7cDf+GTsVxGztWoWdmBpziCY32aiw K3PcgYg1pO4oxZ0/4QcgN4EatGCrkqGwb8o+0BFPkOw5dtn+0DNVAIT+AvIO3ErALm BncYn7mzzb39C/e7fInqH38Oihi1WsRTo7H3fShyqr0c91Ua2P76nFdwVR1UGx5FHS lSMTQk19ivZj8rQajRMB/keknSdXH3QaVQP/hmgZkl9hhJ8QoUIE5yly+YqC+EqHDR w4P//ZBGHGKLw== From: Eric Biggers To: fstests@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Gaurav Kashyap Subject: [RFC PATCH 8/8] generic: verify ciphertext with hardware-wrapped keys Date: Sun, 27 Feb 2022 23:47:22 -0800 Message-Id: <20220228074722.77008-9-ebiggers@kernel.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220228074722.77008-1-ebiggers@kernel.org> References: <20220228074722.77008-1-ebiggers@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers Add two tests which verify that encrypted files are encrypted correctly when a hardware-wrapped inline encryption key is used. The two tests are identical except that one uses FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 and the other uses FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32. These cover both of the settings where hardware-wrapped keys currently may be used. I've verified that these tests run and pass when all prerequisites are met, namely: - Hardware supporting the feature must be present. I tested this on the SM8350 HDK (note: this currently requires a custom TrustZone image); this hardware is compatible with both of IV_INO_LBLK_{64,32}. - The kernel patches for hardware-wrapped key support must be applied. - The filesystem must be ext4 or f2fs. - The kernel must have CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y. - The fscryptctl program must be available, and must have patches for hardware-wrapped key support applied. Signed-off-by: Eric Biggers --- tests/generic/900 | 30 ++++++++++++++++++++++++++++++ tests/generic/900.out | 6 ++++++ tests/generic/901 | 30 ++++++++++++++++++++++++++++++ tests/generic/901.out | 6 ++++++ 4 files changed, 72 insertions(+) create mode 100755 tests/generic/900 create mode 100644 tests/generic/900.out create mode 100755 tests/generic/901 create mode 100644 tests/generic/901.out diff --git a/tests/generic/900 b/tests/generic/900 new file mode 100755 index 00000000..a021732e --- /dev/null +++ b/tests/generic/900 @@ -0,0 +1,30 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright 2022 Google LLC +# +# FS QA Test No. 900 +# +# Verify the ciphertext for encryption policies that use the HW_WRAPPED_KEY and +# IV_INO_LBLK_64 flags and that use AES-256-XTS to encrypt file contents and +# AES-256-CTS-CBC to encrypt file names. +# +. ./common/preamble +_begin_fstest auto quick encrypt + +# Import common functions. +. ./common/filter +. ./common/encrypt + +# real QA test starts here +_supported_fs generic + +# Hardware-wrapped keys require the inlinecrypt mount option. +_require_scratch_inlinecrypt +export MOUNT_OPTIONS="$MOUNT_OPTIONS -o inlinecrypt" + +_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC \ + v2 iv_ino_lblk_64 hw_wrapped_key + +# success, all done +status=0 +exit diff --git a/tests/generic/900.out b/tests/generic/900.out new file mode 100644 index 00000000..9edc012c --- /dev/null +++ b/tests/generic/900.out @@ -0,0 +1,6 @@ +QA output created by 900 + +Verifying ciphertext with parameters: + contents_encryption_mode: AES-256-XTS + filenames_encryption_mode: AES-256-CTS-CBC + options: v2 iv_ino_lblk_64 hw_wrapped_key diff --git a/tests/generic/901 b/tests/generic/901 new file mode 100755 index 00000000..dd5c6e5f --- /dev/null +++ b/tests/generic/901 @@ -0,0 +1,30 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright 2022 Google LLC +# +# FS QA Test No. 901 +# +# Verify the ciphertext for encryption policies that use the HW_WRAPPED_KEY and +# IV_INO_LBLK_32 flags and that use AES-256-XTS to encrypt file contents and +# AES-256-CTS-CBC to encrypt file names. +# +. ./common/preamble +_begin_fstest auto quick encrypt + +# Import common functions. +. ./common/filter +. ./common/encrypt + +# real QA test starts here +_supported_fs generic + +# Hardware-wrapped keys require the inlinecrypt mount option. +_require_scratch_inlinecrypt +export MOUNT_OPTIONS="$MOUNT_OPTIONS -o inlinecrypt" + +_verify_ciphertext_for_encryption_policy AES-256-XTS AES-256-CTS-CBC \ + v2 iv_ino_lblk_32 hw_wrapped_key + +# success, all done +status=0 +exit diff --git a/tests/generic/901.out b/tests/generic/901.out new file mode 100644 index 00000000..2f928465 --- /dev/null +++ b/tests/generic/901.out @@ -0,0 +1,6 @@ +QA output created by 901 + +Verifying ciphertext with parameters: + contents_encryption_mode: AES-256-XTS + filenames_encryption_mode: AES-256-CTS-CBC + options: v2 iv_ino_lblk_32 hw_wrapped_key