xfstests: fscrypt test cleanups
Eric Biggers
maintainerv2 cover xfstests: fscrypt test cleanups
This series makes some minor improvements to the fscrypt ciphertext verification tests. I've split these out from my series "[RFC PATCH 0/8] xfstests: test the fscrypt hardware-wrapped key support"
(https://lore.kernel.org/fstests/[email protected]/T/#u)
and updated them slightly. These can be applied now.
I've verified that all the 'encrypt' group tests still pass with this series applied.
Eric Biggers (5):
fscrypt-crypt-util: use an explicit --direct-key option
fscrypt-crypt-util: refactor get_key_and_iv()
fscrypt-crypt-util: add support for dumping key identifier common/encrypt: log full ciphertext verification params common/encrypt: verify the key identifiers
common/encrypt | 34 +++++-- src/fscrypt-crypt-util.c | 193 ++++++++++++++++++++++++++------------- 2 files changed, 159 insertions(+), 68 deletions(-)
Signature · 1 line
2.35.1
Eric Biggers
maintainerPATCH v2 1/5 fscrypt-crypt-util: use an explicit --direct-key option
From: Eric Biggers <[email protected]>
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, or --kdf=none being given in combination with --file-nonce.
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).
--- common/encrypt | 10 ++++---- src/fscrypt-crypt-util.c | 52 ++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 26 deletions(-)
common/encrypt +4 -6
@@ -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
src/fscrypt-crypt-util.c +32 -20
138 lines not on this page · raw message
Signature · 1 line
2.35.1
Eric Biggers
maintainerPATCH v2 2/5 fscrypt-crypt-util: refactor get_key_and_iv()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
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.
--- src/fscrypt-crypt-util.c | 124 ++++++++++++++++++++++----------------- 1 file changed, 70 insertions(+), 54 deletions(-)
src/fscrypt-crypt-util.c +70 -54
@@ -1818,6 +1818,9 @@ static u32 hash_inode_number(const struct key_and_iv_params *params) } hash_key; info[8 ] =HKDF_CONTEXT_INODE_HASH_KEY ; + +if (params->kdf !=KDF_HKDF_SHA512 ) +die ("--iv-ino-lblk-32 requires --kdf=HKDF-SHA512" );hkdf_sha512 (params->master_key, params->master_key_size,NULL ,0 , info,sizeof (info), hash_key.bytes,sizeof (hash_key));
@@ -1828,16 +1831,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 +1841,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,31 +1857,35 @@ 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 with KDF 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 with KDF requires --mode-num" ); +if (!params->fs_uuid_specified) +die ("--iv-ino-lblk-64 with KDF 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 with KDF requires --mode-num" ); +if (!params->fs_uuid_specified) +die ("--iv-ino-lblk-32 with KDF 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) { + }else { +if (!params->file_nonce_specified) +die ("--kdf=HKDF-SHA512 requires --file-nonce or --iv-ino-lblk-{64,32}" ); info[infolen++] =HKDF_CONTEXT_PER_FILE_ENC_KEY ;memcpy (&info[infolen], params->file_nonce,FILE_NONCE_SIZE ); infolen +=FILE_NONCE_SIZE ; - }else { -die ("--kdf=HKDF-SHA512 requires --file-nonce or --iv-ino-lblk-{64,32}" ); }hkdf_sha512 (params->master_key, params->master_key_size,NULL ,0 , info, infolen, real_key, real_key_size);
@@ -1928,9 +1893,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 {
Signature · 1 line
2.35.1
Eric Biggers
maintainerPATCH v2 3/5 fscrypt-crypt-util: add support for dumping key identifier
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
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.
--- src/fscrypt-crypt-util.c | 51 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-)
src/fscrypt-crypt-util.c +46 -5
@@ -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"
@@ -1949,11 +1951,38 @@ 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; + + info[8 ] =HKDF_CONTEXT_KEY_IDENTIFIER ; + +if (params->kdf !=KDF_HKDF_SHA512 ) +die ("--dump-key-identifier requires --kdf=HKDF-SHA512" ); +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]); +} + +static void parse_master_key (const char *arg,struct key_and_iv_params *params) +{ + params->master_key_size =hex2bin (arg, params->master_key, +MAX_KEY_SIZE ); +if (params->master_key_size <0 ) +die ("Invalid master_key: %s" , arg); +} +enum {OPT_BLOCK_NUMBER ,OPT_BLOCK_SIZE ,OPT_DECRYPT ,OPT_DIRECT_KEY , +OPT_DUMP_KEY_IDENTIFIER ,OPT_FILE_NONCE ,OPT_FS_UUID ,OPT_HELP ,
@@ -1970,6 +1999,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 },
@@ -1986,6 +2016,7 @@ 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;
@@ -2027,6 +2058,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 )
@@ -2074,6 +2108,15 @@ int main(int argc, char *argv[]) argc -= optind; argv += optind; +if (dump_key_identifier) { +if (argc !=1 ) { +usage (stderr); +return 2 ; + } +parse_master_key (argv[0 ], ¶ms); +do_dump_key_identifier (¶ms); +return 0 ; + }if (argc !=2 ) {usage (stderr);return 2 ;
@@ -2087,10 +2130,8 @@ int main(int argc, char *argv[])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, -MAX_KEY_SIZE ); -if (params.master_key_size <0 ) -die ("Invalid master_key: %s" , argv[1 ]); +parse_master_key (argv[1 ], ¶ms); +if (params.master_key_size < cipher->keysize)die ("Master key is too short for cipher %s" , cipher->name);
Signature · 1 line
2.35.1
Eric Biggers
maintainerPATCH v2 4/5 common/encrypt: log full ciphertext verification params
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
To help with debugging, log some additional information to $seqres.full.
--- common/encrypt | 11 +++++++++++ 1 file changed, 11 insertions(+)
common/encrypt +11 -0
@@ -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 <<EOF +Full ciphertext verification parameters: + contents_encryption_mode = $contents_encryption_mode + filenames_encryption_mode = $filenames_encryption_mode + policy_flags = $policy_flags + set_encpolicy_args = $set_encpolicy_args + keyspec = $keyspec + raw_key_hex = $raw_key_hex + crypt_util_contents_args = $crypt_util_contents_args + crypt_util_filename_args = $crypt_util_filename_args +EOF _do_verify_ciphertext_for_encryption_policy \ "$contents_encryption_mode" \ "$filenames_encryption_mode" \
Signature · 1 line
2.35.1
Eric Biggers
maintainerPATCH v2 5/5 common/encrypt: verify the key identifiers
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
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.
--- common/encrypt | 13 +++++++++++++ 1 file changed, 13 insertions(+)
common/encrypt +13 -0
@@ -812,6 +812,7 @@ _verify_ciphertext_for_encryption_policy() local crypt_util_args="" local crypt_util_contents_args="" local crypt_util_filename_args="" + local expected_identifier shift 2 for opt; do
@@ -902,6 +903,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"
Signature · 1 line
2.35.1