fscrypt-crypt-util treats the "block number" part of the IV as 64-bit
when incrementing it. That's wrong for --iv-ino-lblk-32 and
--iv-ino-lblk-64, as in those cases the block number should be 32-bit.
Fix this by using the correct length for the block number.
For --iv-ino-lblk-64 this doesn't actually matter, since in that case
the block number starts at 0 and never exceeds UINT32_MAX.
But for --iv-ino-lblk-32, the hashed inode number gets added to the
original block number to produce the IV block number, which can later
wrap around from UINT32_MAX to 0. As a result, this change fixes
generic/602, though since the wraparound case isn't specifically tested
currently, the chance of failure was extremely small.
Signed-off-by: Eric Biggers <ebiggers@google.com>
Reviewed-by: Daeho Jeong <daeho43@gmail.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
-struct fscrypt_iv {
- union {
- __le64 block_num;
- u8 bytes[32];
+union fscrypt_iv {
+ /* usual IV format */
+ struct {
+ /* logical block number within the file */
+ __le64 block_number;
+
+ /* per-file nonce; only set in DIRECT_KEY mode */
+ u8 nonce[FILE_NONCE_SIZE];
+ };
+ /* IV format for IV_INO_LBLK_* modes */
+ struct {
+ /*
+ * IV_INO_LBLK_64: logical block number within the file
+ * IV_INO_LBLK_32: hashed inode number + logical block number
+ * within the file, mod 2^32
+ */
+ __le32 block_number32;
+
+ /* IV_INO_LBLK_64: inode number */
+ __le32 inode_number;
};
};
static void crypt_loop(const struct fscrypt_cipher *cipher, const u8 *key,
};
};
static void crypt_loop(const struct fscrypt_cipher *cipher, const u8 *key,
- struct fscrypt_iv *iv, bool decrypting,
- size_t block_size, size_t padding)
+ union fscrypt_iv *iv, bool decrypting,
+ size_t block_size, size_t padding, bool is_bnum_32bit)
{
u8 *buf = xmalloc(block_size);
size_t res;
{
u8 *buf = xmalloc(block_size);
size_t res;
memset(&buf[res], 0, crypt_len - res);
if (decrypting)
memset(&buf[res], 0, crypt_len - res);
if (decrypting)
- cipher->decrypt(key, iv->bytes, buf, buf, crypt_len);
+ cipher->decrypt(key, (u8 *)iv, buf, buf, crypt_len);
- cipher->encrypt(key, iv->bytes, buf, buf, crypt_len);
+ cipher->encrypt(key, (u8 *)iv, buf, buf, crypt_len);
full_write(STDOUT_FILENO, buf, crypt_len);
full_write(STDOUT_FILENO, buf, crypt_len);
- iv->block_num = cpu_to_le64(le64_to_cpu(iv->block_num) + 1);
+ if (is_bnum_32bit)
+ iv->block_number32 = cpu_to_le32(
+ le32_to_cpu(iv->block_number32) + 1);
+ else
+ iv->block_number = cpu_to_le64(
+ le64_to_cpu(iv->block_number) + 1);
*/
static void get_key_and_iv(const struct key_and_iv_params *params,
u8 *real_key, size_t real_key_size,
*/
static void get_key_and_iv(const struct key_and_iv_params *params,
u8 *real_key, size_t real_key_size,
{
bool file_nonce_in_iv = false;
struct aes_key aes_key;
{
bool file_nonce_in_iv = false;
struct aes_key aes_key;
info[infolen++] = params->mode_num;
memcpy(&info[infolen], params->fs_uuid, UUID_SIZE);
infolen += UUID_SIZE;
info[infolen++] = params->mode_num;
memcpy(&info[infolen], params->fs_uuid, UUID_SIZE);
infolen += UUID_SIZE;
- put_unaligned_le32(params->inode_number, &iv->bytes[4]);
+ iv->inode_number = cpu_to_le32(params->inode_number);
} else if (params->iv_ino_lblk_32) {
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;
} else if (params->iv_ino_lblk_32) {
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;
- put_unaligned_le32(hash_inode_number(params),
- iv->bytes);
+ iv->block_number32 =
+ cpu_to_le32(hash_inode_number(params));
} else if (params->mode_num != 0) {
info[infolen++] = HKDF_CONTEXT_DIRECT_KEY;
info[infolen++] = params->mode_num;
} else if (params->mode_num != 0) {
info[infolen++] = HKDF_CONTEXT_DIRECT_KEY;
info[infolen++] = params->mode_num;
}
if (file_nonce_in_iv && params->file_nonce_specified)
}
if (file_nonce_in_iv && params->file_nonce_specified)
- memcpy(&iv->bytes[8], params->file_nonce, FILE_NONCE_SIZE);
+ memcpy(iv->nonce, params->file_nonce, FILE_NONCE_SIZE);
size_t padding = 0;
const struct fscrypt_cipher *cipher;
u8 real_key[MAX_KEY_SIZE];
size_t padding = 0;
const struct fscrypt_cipher *cipher;
u8 real_key[MAX_KEY_SIZE];
get_key_and_iv(¶ms, real_key, cipher->keysize, &iv);
get_key_and_iv(¶ms, real_key, cipher->keysize, &iv);
- crypt_loop(cipher, real_key, &iv, decrypting, block_size, padding);
+ crypt_loop(cipher, real_key, &iv, decrypting, block_size, padding,
+ params.iv_ino_lblk_64 || params.iv_ino_lblk_32);