crypto: vmac - add nonced version with big endian digest
authorEric Biggers <ebiggers@google.com>
Mon, 18 Jun 2018 17:22:39 +0000 (10:22 -0700)
committerHerbert Xu <herbert@gondor.apana.org.au>
Sun, 1 Jul 2018 13:00:43 +0000 (21:00 +0800)
Currently the VMAC template uses a "nonce" hardcoded to 0, which makes
it insecure unless a unique key is set for every message.  Also, the
endianness of the final digest is wrong: the implementation uses little
endian, but the VMAC specification has it as big endian, as do other
VMAC implementations such as the one in Crypto++.

Add a new VMAC template where the nonce is passed as the first 16 bytes
of data (similar to what is done for Poly1305's nonce), and the digest
is big endian.  Call it "vmac64", since the old name of simply "vmac"
didn't clarify whether the implementation is of VMAC-64 or of VMAC-128
(which produce 64-bit and 128-bit digests respectively); so we fix the
naming ambiguity too.

Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
crypto/testmgr.c
crypto/testmgr.h
crypto/vmac.c

index 11e45352fd0b825e25e7a80259a9c5c24bfc20f1..60a557b0f8d39a1d17edb52dbe556afbe763cb7c 100644 (file)
@@ -3483,6 +3483,12 @@ static const struct alg_test_desc alg_test_descs[] = {
                .suite = {
                        .hash = __VECS(aes_vmac128_tv_template)
                }
+       }, {
+               .alg = "vmac64(aes)",
+               .test = alg_test_hash,
+               .suite = {
+                       .hash = __VECS(vmac64_aes_tv_template)
+               }
        }, {
                .alg = "wp256",
                .test = alg_test_hash,
index b950aa234e43f6fad863d0741f4df98f0411648c..7b022c47a6231ed46f51920312cdf80e6a384742 100644 (file)
@@ -4705,6 +4705,161 @@ static const struct hash_testvec aes_vmac128_tv_template[] = {
        },
 };
 
+static const char vmac64_string1[144] = {
+       '\0',     '\0',   '\0',   '\0',   '\0',   '\0',   '\0',   '\0',
+       '\0',     '\0',   '\0',   '\0',   '\0',   '\0',   '\0',   '\0',
+       '\x01', '\x01', '\x01', '\x01', '\x02', '\x03', '\x02', '\x02',
+       '\x02', '\x04', '\x01', '\x07', '\x04', '\x01', '\x04', '\x03',
+};
+
+static const char vmac64_string2[144] = {
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+        'a',  'b',  'c',
+};
+
+static const char vmac64_string3[144] = {
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+        'a',  'b',  'c',  'a',  'b',  'c',  'a',  'b',
+        'c',  'a',  'b',  'c',  'a',  'b',  'c',  'a',
+        'b',  'c',  'a',  'b',  'c',  'a',  'b',  'c',
+        'a',  'b',  'c',  'a',  'b',  'c',  'a',  'b',
+        'c',  'a',  'b',  'c',  'a',  'b',  'c',  'a',
+        'b',  'c',  'a',  'b',  'c',  'a',  'b',  'c',
+};
+
+static const char vmac64_string4[33] = {
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+       'b',   'c',  'e',  'f',  'i',  'j',  'l',  'm',
+       'o',   'p',  'r',  's',  't',  'u',  'w',  'x',
+       'z',
+};
+
+static const char vmac64_string5[143] = {
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+        'r',  'm',  'b',  't',  'c',  'o',  'l',  'k',
+        ']',  '%',  '9',  '2',  '7',  '!',  'A',
+};
+
+static const char vmac64_string6[145] = {
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
+        'p',  't',  '*',  '7',  'l',  'i',  '!',  '#',
+        'w',  '0',  'z',  '/',  '4',  'A',  'n',
+};
+
+static const struct hash_testvec vmac64_aes_tv_template[] = {
+       { /* draft-krovetz-vmac-01 test vector 1 */
+               .key    = "abcdefghijklmnop",
+               .ksize  = 16,
+               .plaintext = "\0\0\0\0\0\0\0\0bcdefghi",
+               .psize  = 16,
+               .digest = "\x25\x76\xbe\x1c\x56\xd8\xb8\x1b",
+       }, { /* draft-krovetz-vmac-01 test vector 2 */
+               .key    = "abcdefghijklmnop",
+               .ksize  = 16,
+               .plaintext = "\0\0\0\0\0\0\0\0bcdefghiabc",
+               .psize  = 19,
+               .digest = "\x2d\x37\x6c\xf5\xb1\x81\x3c\xe5",
+       }, { /* draft-krovetz-vmac-01 test vector 3 */
+               .key    = "abcdefghijklmnop",
+               .ksize  = 16,
+               .plaintext = "\0\0\0\0\0\0\0\0bcdefghi"
+                         "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc",
+               .psize  = 64,
+               .digest = "\xe8\x42\x1f\x61\xd5\x73\xd2\x98",
+       }, { /* draft-krovetz-vmac-01 test vector 4 */
+               .key    = "abcdefghijklmnop",
+               .ksize  = 16,
+               .plaintext = "\0\0\0\0\0\0\0\0bcdefghi"
+                         "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
+                         "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
+                         "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
+                         "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
+                         "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"
+                         "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabc",
+               .psize  = 316,
+               .digest = "\x44\x92\xdf\x6c\x5c\xac\x1b\xbe",
+               .tap    = { 1, 100, 200, 15 },
+               .np     = 4,
+       }, {
+               .key    = "\x00\x01\x02\x03\x04\x05\x06\x07"
+                         "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+               .ksize  = 16,
+               .plaintext = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .psize  = 16,
+               .digest = "\x54\x7b\xa4\x77\x35\x80\x58\x07",
+       }, {
+               .key    = "\x00\x01\x02\x03\x04\x05\x06\x07"
+                         "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+               .ksize  = 16,
+               .plaintext = vmac64_string1,
+               .psize  = sizeof(vmac64_string1),
+               .digest = "\xa1\x8c\x68\xae\xd3\x3c\xf5\xce",
+       }, {
+               .key    = "\x00\x01\x02\x03\x04\x05\x06\x07"
+                         "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+               .ksize  = 16,
+               .plaintext = vmac64_string2,
+               .psize  = sizeof(vmac64_string2),
+               .digest = "\x2d\x14\xbd\x81\x73\xb0\x27\xc9",
+       }, {
+               .key    = "\x00\x01\x02\x03\x04\x05\x06\x07"
+                         "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
+               .ksize  = 16,
+               .plaintext = vmac64_string3,
+               .psize  = sizeof(vmac64_string3),
+               .digest = "\x19\x0b\x47\x98\x8c\x95\x1a\x8d",
+       }, {
+               .key    = "abcdefghijklmnop",
+               .ksize  = 16,
+               .plaintext = "\x00\x00\x00\x00\x00\x00\x00\x00"
+                         "\x00\x00\x00\x00\x00\x00\x00\x00",
+               .psize  = 16,
+               .digest = "\x84\x8f\x55\x9e\x26\xa1\x89\x3b",
+       }, {
+               .key    = "abcdefghijklmnop",
+               .ksize  = 16,
+               .plaintext = vmac64_string1,
+               .psize  = sizeof(vmac64_string1),
+               .digest = "\xc2\x74\x8d\xf6\xb0\xab\x5e\xab",
+       }, {
+               .key    = "abcdefghijklmnop",
+               .ksize  = 16,
+               .plaintext = vmac64_string2,
+               .psize  = sizeof(vmac64_string2),
+               .digest = "\xdf\x09\x7b\x3d\x42\x68\x15\x11",
+       }, {
+               .key    = "abcdefghijklmnop",
+               .ksize  = 16,
+               .plaintext = vmac64_string3,
+               .psize  = sizeof(vmac64_string3),
+               .digest = "\xd4\xfa\x8f\xed\xe1\x8f\x32\x8b",
+       }, {
+               .key    = "a09b5cd!f#07K\x00\x00\x00",
+               .ksize  = 16,
+               .plaintext = vmac64_string4,
+               .psize  = sizeof(vmac64_string4),
+               .digest = "\x5f\xa1\x4e\x42\xea\x0f\xa5\xab",
+       }, {
+               .key    = "a09b5cd!f#07K\x00\x00\x00",
+               .ksize  = 16,
+               .plaintext = vmac64_string5,
+               .psize  = sizeof(vmac64_string5),
+               .digest = "\x60\x67\xe8\x1d\xbc\x98\x31\x25",
+       }, {
+               .key    = "a09b5cd!f#07K\x00\x00\x00",
+               .ksize  = 16,
+               .plaintext = vmac64_string6,
+               .psize  = sizeof(vmac64_string6),
+               .digest = "\x41\xeb\x65\x95\x47\x9b\xae\xc4",
+       },
+};
+
 /*
  * SHA384 HMAC test vectors from RFC4231
  */
index bb2fc787d61568d3c0a871f1fc2e04cfc0732437..bf1e385bc6847f73c4ad0a4cf43abd6cb32537e6 100644 (file)
@@ -45,6 +45,7 @@
 #define VMAC_KEY_SIZE  128/* Must be 128, 192 or 256                   */
 #define VMAC_KEY_LEN   (VMAC_KEY_SIZE/8)
 #define VMAC_NHBYTES   128/* Must 2^i for any 3 < i < 13 Standard = 128*/
+#define VMAC_NONCEBYTES        16
 
 /* per-transform (per-key) context */
 struct vmac_tfm_ctx {
@@ -63,6 +64,11 @@ struct vmac_desc_ctx {
        unsigned int partial_size;      /* size of the partial block */
        bool first_block_processed;
        u64 polytmp[2*VMAC_TAG_LEN/64]; /* running total of L2-hash */
+       union {
+               u8 bytes[VMAC_NONCEBYTES];
+               __be64 pads[VMAC_NONCEBYTES / 8];
+       } nonce;
+       unsigned int nonce_size; /* nonce bytes filled so far */
 };
 
 /*
@@ -480,6 +486,17 @@ static int vmac_init(struct shash_desc *desc)
        dctx->partial_size = 0;
        dctx->first_block_processed = false;
        memcpy(dctx->polytmp, tctx->polykey, sizeof(dctx->polytmp));
+       dctx->nonce_size = 0;
+       return 0;
+}
+
+static int vmac_init_with_hardcoded_nonce(struct shash_desc *desc)
+{
+       struct vmac_desc_ctx *dctx = shash_desc_ctx(desc);
+
+       vmac_init(desc);
+       memset(&dctx->nonce, 0, VMAC_NONCEBYTES);
+       dctx->nonce_size = VMAC_NONCEBYTES;
        return 0;
 }
 
@@ -489,6 +506,15 @@ static int vmac_update(struct shash_desc *desc, const u8 *p, unsigned int len)
        struct vmac_desc_ctx *dctx = shash_desc_ctx(desc);
        unsigned int n;
 
+       /* Nonce is passed as first VMAC_NONCEBYTES bytes of data */
+       if (dctx->nonce_size < VMAC_NONCEBYTES) {
+               n = min(len, VMAC_NONCEBYTES - dctx->nonce_size);
+               memcpy(&dctx->nonce.bytes[dctx->nonce_size], p, n);
+               dctx->nonce_size += n;
+               p += n;
+               len -= n;
+       }
+
        if (dctx->partial_size) {
                n = min(len, VMAC_NHBYTES - dctx->partial_size);
                memcpy(&dctx->partial[dctx->partial_size], p, n);
@@ -544,30 +570,62 @@ static u64 vhash_final(const struct vmac_tfm_ctx *tctx,
        return l3hash(ch, cl, tctx->l3key[0], tctx->l3key[1], partial * 8);
 }
 
-static int vmac_final(struct shash_desc *desc, u8 *out)
+static int __vmac_final(struct shash_desc *desc, u64 *mac)
 {
        const struct vmac_tfm_ctx *tctx = crypto_shash_ctx(desc->tfm);
        struct vmac_desc_ctx *dctx = shash_desc_ctx(desc);
-       static const u8 nonce[16] = {}; /* TODO: this is insecure */
-       union {
-               u8 bytes[16];
-               __be64 pads[2];
-       } block;
        int index;
        u64 hash, pad;
 
+       if (dctx->nonce_size != VMAC_NONCEBYTES)
+               return -EINVAL;
+
+       /*
+        * The VMAC specification requires a nonce at least 1 bit shorter than
+        * the block cipher's block length, so we actually only accept a 127-bit
+        * nonce.  We define the unused bit to be the first one and require that
+        * it be 0, so the needed prepending of a 0 bit is implicit.
+        */
+       if (dctx->nonce.bytes[0] & 0x80)
+               return -EINVAL;
+
        /* Finish calculating the VHASH of the message */
        hash = vhash_final(tctx, dctx);
 
        /* Generate pseudorandom pad by encrypting the nonce */
-       memcpy(&block, nonce, 16);
-       index = block.bytes[15] & 1;
-       block.bytes[15] &= ~1;
-       crypto_cipher_encrypt_one(tctx->cipher, block.bytes, block.bytes);
-       pad = be64_to_cpu(block.pads[index]);
+       BUILD_BUG_ON(VMAC_NONCEBYTES != 2 * (VMAC_TAG_LEN / 8));
+       index = dctx->nonce.bytes[VMAC_NONCEBYTES - 1] & 1;
+       dctx->nonce.bytes[VMAC_NONCEBYTES - 1] &= ~1;
+       crypto_cipher_encrypt_one(tctx->cipher, dctx->nonce.bytes,
+                                 dctx->nonce.bytes);
+       pad = be64_to_cpu(dctx->nonce.pads[index]);
 
        /* The VMAC is the sum of VHASH and the pseudorandom pad */
-       put_unaligned_le64(hash + pad, out);
+       *mac = hash + pad;
+       return 0;
+}
+
+static int vmac_final_le(struct shash_desc *desc, u8 *out)
+{
+       u64 mac;
+       int err;
+
+       err = __vmac_final(desc, &mac);
+       if (err)
+               return err;
+       put_unaligned_le64(mac, out);
+       return 0;
+}
+
+static int vmac_final_be(struct shash_desc *desc, u8 *out)
+{
+       u64 mac;
+       int err;
+
+       err = __vmac_final(desc, &mac);
+       if (err)
+               return err;
+       put_unaligned_be64(mac, out);
        return 0;
 }
 
@@ -593,7 +651,8 @@ static void vmac_exit_tfm(struct crypto_tfm *tfm)
        crypto_free_cipher(tctx->cipher);
 }
 
-static int vmac_create(struct crypto_template *tmpl, struct rtattr **tb)
+static int vmac_create_common(struct crypto_template *tmpl, struct rtattr **tb,
+                             bool vmac64)
 {
        struct shash_instance *inst;
        struct crypto_alg *alg;
@@ -609,10 +668,10 @@ static int vmac_create(struct crypto_template *tmpl, struct rtattr **tb)
                return PTR_ERR(alg);
 
        err = -EINVAL;
-       if (alg->cra_blocksize != 16)
+       if (alg->cra_blocksize != VMAC_NONCEBYTES)
                goto out_put_alg;
 
-       inst = shash_alloc_instance("vmac", alg);
+       inst = shash_alloc_instance(tmpl->name, alg);
        err = PTR_ERR(inst);
        if (IS_ERR(inst))
                goto out_put_alg;
@@ -633,9 +692,15 @@ static int vmac_create(struct crypto_template *tmpl, struct rtattr **tb)
 
        inst->alg.descsize = sizeof(struct vmac_desc_ctx);
        inst->alg.digestsize = VMAC_TAG_LEN / 8;
-       inst->alg.init = vmac_init;
+       if (vmac64) {
+               inst->alg.init = vmac_init;
+               inst->alg.final = vmac_final_be;
+       } else {
+               pr_warn("vmac: using insecure hardcoded nonce\n");
+               inst->alg.init = vmac_init_with_hardcoded_nonce;
+               inst->alg.final = vmac_final_le;
+       }
        inst->alg.update = vmac_update;
-       inst->alg.final = vmac_final;
        inst->alg.setkey = vmac_setkey;
 
        err = shash_register_instance(tmpl, inst);
@@ -649,6 +714,16 @@ out_put_alg:
        return err;
 }
 
+static int vmac_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+       return vmac_create_common(tmpl, tb, false);
+}
+
+static int vmac64_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+       return vmac_create_common(tmpl, tb, true);
+}
+
 static struct crypto_template vmac_tmpl = {
        .name = "vmac",
        .create = vmac_create,
@@ -656,14 +731,32 @@ static struct crypto_template vmac_tmpl = {
        .module = THIS_MODULE,
 };
 
+static struct crypto_template vmac64_tmpl = {
+       .name = "vmac64",
+       .create = vmac64_create,
+       .free = shash_free_instance,
+       .module = THIS_MODULE,
+};
+
 static int __init vmac_module_init(void)
 {
-       return crypto_register_template(&vmac_tmpl);
+       int err;
+
+       err = crypto_register_template(&vmac_tmpl);
+       if (err)
+               return err;
+
+       err = crypto_register_template(&vmac64_tmpl);
+       if (err)
+               crypto_unregister_template(&vmac_tmpl);
+
+       return err;
 }
 
 static void __exit vmac_module_exit(void)
 {
        crypto_unregister_template(&vmac_tmpl);
+       crypto_unregister_template(&vmac64_tmpl);
 }
 
 module_init(vmac_module_init);
@@ -672,3 +765,4 @@ module_exit(vmac_module_exit);
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("VMAC hash algorithm");
 MODULE_ALIAS_CRYPTO("vmac");
+MODULE_ALIAS_CRYPTO("vmac64");