fscrypt-crypt-util: fix maximum IV size
[xfstests-dev.git] / src / fscrypt-crypt-util.c
index d9189346cbbce3594e93a493ddb3dd518513b5a6..03cc3c4a12997734195a455c1bfd2ba249525c5c 100644 (file)
@@ -59,6 +59,8 @@ static void usage(FILE *fp)
 "WARNING: this program is only meant for testing, not for \"real\" use!\n"
 "\n"
 "Options:\n"
+"  --block-number=BNUM         Starting block number for IV generation.\n"
+"                                Default: 0\n"
 "  --block-size=BLOCK_SIZE     Encrypt each BLOCK_SIZE bytes independently.\n"
 "                                Default: 4096 bytes\n"
 "  --decrypt                   Decrypt instead of encrypt\n"
@@ -1640,6 +1642,7 @@ static u64 siphash_1u64(const u64 key[2], u64 data)
 #define FILE_NONCE_SIZE                16
 #define UUID_SIZE              16
 #define MAX_KEY_SIZE           64
+#define MAX_IV_SIZE            ADIANTUM_IV_SIZE
 
 static const struct fscrypt_cipher {
        const char *name;
@@ -1692,16 +1695,34 @@ static const struct fscrypt_cipher *find_fscrypt_cipher(const char *name)
        return NULL;
 }
 
-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;
+       };
+       /* Any extra bytes up to the algorithm's IV size must be zeroed */
+       u8 bytes[MAX_IV_SIZE];
 };
 
 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;
@@ -1724,7 +1745,12 @@ static void crypt_loop(const struct fscrypt_cipher *cipher, const u8 *key,
 
                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);
        }
        free(buf);
 }
@@ -1766,6 +1792,7 @@ struct key_and_iv_params {
        bool file_nonce_specified;
        bool iv_ino_lblk_64;
        bool iv_ino_lblk_32;
+       u64 block_number;
        u64 inode_number;
        u8 fs_uuid[UUID_SIZE];
        bool fs_uuid_specified;
@@ -1806,7 +1833,7 @@ static u32 hash_inode_number(const struct key_and_iv_params *params)
  */
 static void get_key_and_iv(const struct key_and_iv_params *params,
                           u8 *real_key, size_t real_key_size,
-                          struct fscrypt_iv *iv)
+                          union fscrypt_iv *iv)
 {
        bool file_nonce_in_iv = false;
        struct aes_key aes_key;
@@ -1818,6 +1845,9 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
 
        memset(iv, 0, sizeof(*iv));
 
+       /* 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) {
                const char *opt = params->iv_ino_lblk_64 ? "--iv-ino-lblk-64" :
                                                           "--iv-ino-lblk-32";
@@ -1831,6 +1861,8 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
                        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);
        }
@@ -1860,14 +1892,17 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
                        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->block_number32 = cpu_to_le32(params->block_number);
+                       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;
-                       put_unaligned_le32(hash_inode_number(params),
-                                          iv->bytes);
+                       iv->block_number32 =
+                               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;
@@ -1888,10 +1923,11 @@ static void get_key_and_iv(const struct key_and_iv_params *params,
        }
 
        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);
 }
 
 enum {
+       OPT_BLOCK_NUMBER,
        OPT_BLOCK_SIZE,
        OPT_DECRYPT,
        OPT_FILE_NONCE,
@@ -1906,6 +1942,7 @@ enum {
 };
 
 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 },
        { "file-nonce",      required_argument, NULL, OPT_FILE_NONCE },
@@ -1928,7 +1965,7 @@ int main(int argc, char *argv[])
        size_t padding = 0;
        const struct fscrypt_cipher *cipher;
        u8 real_key[MAX_KEY_SIZE];
-       struct fscrypt_iv iv;
+       union fscrypt_iv iv;
        char *tmp;
        int c;
 
@@ -1947,6 +1984,12 @@ int main(int argc, char *argv[])
 
        while ((c = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
                switch (c) {
+               case OPT_BLOCK_NUMBER:
+                       errno = 0;
+                       params.block_number = strtoull(optarg, &tmp, 10);
+                       if (*tmp || errno)
+                               die("Invalid block number: %s", optarg);
+                       break;
                case OPT_BLOCK_SIZE:
                        errno = 0;
                        block_size = strtoul(optarg, &tmp, 10);
@@ -2025,6 +2068,7 @@ int main(int argc, char *argv[])
 
        get_key_and_iv(&params, 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);
        return 0;
 }