Add Poly1305-AES (-Camellia, etc) MACs
authorJussi Kivilinna <jussi.kivilinna@iki.fi>
Sun, 11 May 2014 09:00:19 +0000 (12:00 +0300)
committerJussi Kivilinna <jussi.kivilinna@iki.fi>
Mon, 12 May 2014 17:32:44 +0000 (20:32 +0300)
* cipher/mac-internal.h (_gcry_mac_type_spec_poly1305_aes)
(_gcry_mac_type_spec_poly1305_camellia)
(_gcry_mac_type_spec_poly1305_twofish)
(_gcry_mac_type_spec_poly1305_serpent)
(_gcry_mac_type_spec_poly1305_seed): New.
* cipher/mac-poly1305.c (poly1305mac_context_s): Add 'hd' and
'nonce_set'.
(poly1305mac_open, poly1305mac_close, poly1305mac_setkey): Add handling
for Poly1305-*** MACs.
(poly1305mac_prepare_key, poly1305mac_setiv): New.
(poly1305mac_reset, poly1305mac_write, poly1305mac_read): Add handling
for 'nonce_set'.
(poly1305mac_ops): Add 'poly1305mac_setiv'.
(_gcry_mac_type_spec_poly1305_aes)
(_gcry_mac_type_spec_poly1305_camellia)
(_gcry_mac_type_spec_poly1305_twofish)
(_gcry_mac_type_spec_poly1305_serpent)
(_gcry_mac_type_spec_poly1305_seed): New.
* cipher/mac.c (mac_list): Add Poly1305-AES, Poly1305-Twofish,
Poly1305-Serpent, Poly1305-SEED and Poly1305-Camellia.
* src/gcrypt.h.in: Add 'GCRY_MAC_POLY1305_AES',
'GCRY_MAC_POLY1305_CAMELLIA', 'GCRY_MAC_POLY1305_TWOFISH',
'GCRY_MAC_POLY1305_SERPENT' and 'GCRY_MAC_POLY1305_SEED'.
* tests/basic.c (check_mac): Add Poly1305-AES test vectors.
* tests/bench-slope.c (bench_mac_init): Set IV for Poly1305-*** MACs.
* tests/bench-slope.c (mac_bench): Set IV for Poly1305-*** MACs.
--

Patch adds Bernstein's Poly1305-AES message authentication code to libgcrypt
and other variants of Poly1305-<128-bit block cipher>.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
cipher/mac-internal.h
cipher/mac-poly1305.c
cipher/mac.c
src/gcrypt.h.in
tests/basic.c
tests/bench-slope.c
tests/benchmark.c

index 81b6185..f65a8ae 100644 (file)
@@ -217,3 +217,18 @@ extern gcry_mac_spec_t _gcry_mac_type_spec_gmac_camellia;
  * The Poly1305 MAC algorithm specifications (mac-poly1305.c).
  */
 extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac;
+#if USE_AES
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_aes;
+#endif
+#if USE_CAMELLIA
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_camellia;
+#endif
+#if USE_TWOFISH
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_twofish;
+#endif
+#if USE_SERPENT
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_serpent;
+#endif
+#if USE_SEED
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_seed;
+#endif
index e265b64..76b369a 100644 (file)
 
 struct poly1305mac_context_s {
   poly1305_context_t ctx;
+  gcry_cipher_hd_t hd;
   struct {
     unsigned int key_set:1;
+    unsigned int nonce_set:1;
     unsigned int tag:1;
   } marks;
   byte tag[POLY1305_TAGLEN];
@@ -44,6 +46,9 @@ poly1305mac_open (gcry_mac_hd_t h)
 {
   struct poly1305mac_context_s *mac_ctx;
   int secure = (h->magic == CTX_MAGIC_SECURE);
+  unsigned int flags = (secure ? GCRY_CIPHER_SECURE : 0);
+  gcry_err_code_t err;
+  int cipher_algo;
 
   if (secure)
     mac_ctx = xtrycalloc_secure (1, sizeof(*mac_ctx));
@@ -55,14 +60,71 @@ poly1305mac_open (gcry_mac_hd_t h)
 
   h->u.poly1305mac.ctx = mac_ctx;
 
+  switch (h->spec->algo)
+    {
+    default:
+      /* already checked. */
+    case GCRY_MAC_POLY1305:
+      /* plain Poly1305. */
+      cipher_algo = -1;
+      return 0;
+    case GCRY_MAC_POLY1305_AES:
+      cipher_algo = GCRY_CIPHER_AES;
+      break;
+    case GCRY_MAC_POLY1305_CAMELLIA:
+      cipher_algo = GCRY_CIPHER_CAMELLIA128;
+      break;
+    case GCRY_MAC_POLY1305_TWOFISH:
+      cipher_algo = GCRY_CIPHER_TWOFISH;
+      break;
+    case GCRY_MAC_POLY1305_SERPENT:
+      cipher_algo = GCRY_CIPHER_SERPENT128;
+      break;
+    case GCRY_MAC_POLY1305_SEED:
+      cipher_algo = GCRY_CIPHER_SEED;
+      break;
+    }
+
+  err = _gcry_cipher_open_internal (&mac_ctx->hd, cipher_algo,
+                                   GCRY_CIPHER_MODE_ECB, flags);
+  if (err)
+    goto err_free;
+
   return 0;
+
+err_free:
+  xfree(h->u.poly1305mac.ctx);
+  return err;
 }
 
 
 static void
 poly1305mac_close (gcry_mac_hd_t h)
 {
-  xfree(h->u.poly1305mac.ctx);
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+
+  if (h->spec->algo != GCRY_MAC_POLY1305)
+    _gcry_cipher_close (mac_ctx->hd);
+
+  xfree(mac_ctx);
+}
+
+
+static gcry_err_code_t
+poly1305mac_prepare_key (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+  size_t block_keylen = keylen - 16;
+
+  /* Need at least 16 + 1 byte key. */
+  if (keylen <= 16)
+    return GPG_ERR_INV_KEYLEN;
+
+  /* For Poly1305-AES, first part of key is passed to Poly1305 as is. */
+  memcpy (mac_ctx->key, key + block_keylen, 16);
+
+  /* Remaining part is used as key for the block cipher. */
+  return _gcry_cipher_setkey (mac_ctx->hd, key, block_keylen);
 }
 
 
@@ -77,22 +139,74 @@ poly1305mac_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
   memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
 
   mac_ctx->marks.key_set = 0;
+  mac_ctx->marks.nonce_set = 0;
   mac_ctx->marks.tag = 0;
 
-  if (keylen != POLY1305_KEYLEN)
-    return GPG_ERR_INV_KEYLEN;
-
-  memcpy(mac_ctx->key, key, POLY1305_KEYLEN);
+  if (h->spec->algo != GCRY_MAC_POLY1305)
+    {
+      err = poly1305mac_prepare_key (h, key, keylen);
+      if (err)
+        return err;
 
-  err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
-  if (err)
+      /* Poly1305-AES/etc also need nonce. */
+      mac_ctx->marks.key_set = 1;
+      mac_ctx->marks.nonce_set = 0;
+    }
+  else
     {
-      memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
-      return err;
+      /* For plain Poly1305, key is the nonce and setup is complete now. */
+
+      if (keylen != POLY1305_KEYLEN)
+        return GPG_ERR_INV_KEYLEN;
+
+      memcpy (mac_ctx->key, key, keylen);
+
+      err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
+      if (err)
+        {
+          memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
+          return err;
+        }
+
+      mac_ctx->marks.key_set = 1;
+      mac_ctx->marks.nonce_set = 1;
     }
 
-  mac_ctx->marks.key_set = 1;
+  return 0;
+}
+
+
+static gcry_err_code_t
+poly1305mac_setiv (gcry_mac_hd_t h, const unsigned char *iv, size_t ivlen)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+  gcry_err_code_t err;
+
+  if (h->spec->algo == GCRY_MAC_POLY1305)
+    return GPG_ERR_INV_ARG;
+
+  if (ivlen != 16)
+    return GPG_ERR_INV_ARG;
 
+  if (!mac_ctx->marks.key_set)
+    return 0;
+
+  memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
+  memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
+  mac_ctx->marks.nonce_set = 0;
+  mac_ctx->marks.tag = 0;
+
+  /* Prepare second part of the poly1305 key. */
+
+  err = _gcry_cipher_encrypt (mac_ctx->hd, mac_ctx->key + 16, 16, iv, 16);
+  if (err)
+    return err;
+
+  err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
+  if (err)
+    return err;
+
+  mac_ctx->marks.nonce_set = 1;
   return 0;
 }
 
@@ -102,13 +216,14 @@ poly1305mac_reset (gcry_mac_hd_t h)
 {
   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
 
-  if (!mac_ctx->marks.key_set)
+  if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_set)
     return GPG_ERR_INV_STATE;
 
   memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
   memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
 
   mac_ctx->marks.key_set = 1;
+  mac_ctx->marks.nonce_set = 1;
   mac_ctx->marks.tag = 0;
 
   return _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
@@ -120,7 +235,8 @@ poly1305mac_write (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
 {
   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
 
-  if (!mac_ctx->marks.key_set || mac_ctx->marks.tag)
+  if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_set ||
+      mac_ctx->marks.tag)
     return GPG_ERR_INV_STATE;
 
   _gcry_poly1305_update (&mac_ctx->ctx, buf, buflen);
@@ -133,7 +249,7 @@ poly1305mac_read (gcry_mac_hd_t h, unsigned char *outbuf, size_t *outlen)
 {
   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
 
-  if (!mac_ctx->marks.key_set)
+  if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_set)
     return GPG_ERR_INV_STATE;
 
   if (!mac_ctx->marks.tag)
@@ -197,7 +313,7 @@ static gcry_mac_spec_ops_t poly1305mac_ops = {
   poly1305mac_open,
   poly1305mac_close,
   poly1305mac_setkey,
-  NULL,
+  poly1305mac_setiv,
   poly1305mac_reset,
   poly1305mac_write,
   poly1305mac_read,
@@ -211,3 +327,33 @@ gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac = {
   GCRY_MAC_POLY1305, {0, 0}, "POLY1305",
   &poly1305mac_ops
 };
+#if USE_AES
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_aes = {
+  GCRY_MAC_POLY1305_AES, {0, 0}, "POLY1305_AES",
+  &poly1305mac_ops
+};
+#endif
+#if USE_CAMELLIA
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_camellia = {
+  GCRY_MAC_POLY1305_CAMELLIA, {0, 0}, "POLY1305_CAMELLIA",
+  &poly1305mac_ops
+};
+#endif
+#if USE_TWOFISH
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_twofish = {
+  GCRY_MAC_POLY1305_TWOFISH, {0, 0}, "POLY1305_TWOFISH",
+  &poly1305mac_ops
+};
+#endif
+#if USE_SERPENT
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_serpent = {
+  GCRY_MAC_POLY1305_SERPENT, {0, 0}, "POLY1305_SERPENT",
+  &poly1305mac_ops
+};
+#endif
+#if USE_SEED
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_seed = {
+  GCRY_MAC_POLY1305_SEED, {0, 0}, "POLY1305_SEED",
+  &poly1305mac_ops
+};
+#endif
index e583369..30117b9 100644 (file)
@@ -75,14 +75,17 @@ static gcry_mac_spec_t *mac_list[] = {
 #if USE_AES
   &_gcry_mac_type_spec_cmac_aes,
   &_gcry_mac_type_spec_gmac_aes,
+  &_gcry_mac_type_spec_poly1305mac_aes,
 #endif
 #if USE_TWOFISH
   &_gcry_mac_type_spec_cmac_twofish,
   &_gcry_mac_type_spec_gmac_twofish,
+  &_gcry_mac_type_spec_poly1305mac_twofish,
 #endif
 #if USE_SERPENT
   &_gcry_mac_type_spec_cmac_serpent,
   &_gcry_mac_type_spec_gmac_serpent,
+  &_gcry_mac_type_spec_poly1305mac_serpent,
 #endif
 #if USE_RFC2268
   &_gcry_mac_type_spec_cmac_rfc2268,
@@ -90,10 +93,12 @@ static gcry_mac_spec_t *mac_list[] = {
 #if USE_SEED
   &_gcry_mac_type_spec_cmac_seed,
   &_gcry_mac_type_spec_gmac_seed,
+  &_gcry_mac_type_spec_poly1305mac_seed,
 #endif
 #if USE_CAMELLIA
   &_gcry_mac_type_spec_cmac_camellia,
   &_gcry_mac_type_spec_gmac_camellia,
+  &_gcry_mac_type_spec_poly1305mac_camellia,
 #endif
 #ifdef USE_IDEA
   &_gcry_mac_type_spec_cmac_idea,
index 8648e96..3145020 100644 (file)
@@ -1343,7 +1343,12 @@ enum gcry_mac_algos
     GCRY_MAC_GMAC_SERPENT       = 404,
     GCRY_MAC_GMAC_SEED          = 405,
 
-    GCRY_MAC_POLY1305           = 501
+    GCRY_MAC_POLY1305           = 501,
+    GCRY_MAC_POLY1305_AES       = 502,
+    GCRY_MAC_POLY1305_CAMELLIA  = 503,
+    GCRY_MAC_POLY1305_TWOFISH   = 504,
+    GCRY_MAC_POLY1305_SERPENT   = 505,
+    GCRY_MAC_POLY1305_SEED      = 506
   };
 
 /* Flags used with the open function.  */
index cc0f4c1..de10617 100644 (file)
@@ -5596,6 +5596,39 @@ check_mac (void)
         "\x12\x97\x6a\x08\xc4\x42\x6d\x0c\xe8\xa8\x24\x07\xc4\xf4\x82\x07"
         "\x80\xf8\xc2\x0a\xa7\x12\x02\xd1\xe2\x91\x79\xcb\xcb\x55\x5a\x57",
         "\x51\x54\xad\x0d\x2c\xb2\x6e\x01\x27\x4f\xc5\x11\x48\x49\x1f\x1b" },
+      /* from http://cr.yp.to/mac/poly1305-20050329.pdf */
+      { GCRY_MAC_POLY1305_AES,
+        "\xf3\xf6",
+        "\xec\x07\x4c\x83\x55\x80\x74\x17\x01\x42\x5b\x62\x32\x35\xad\xd6"
+        "\x85\x1f\xc4\x0c\x34\x67\xac\x0b\xe0\x5c\xc2\x04\x04\xf3\xf7\x00",
+        "\xf4\xc6\x33\xc3\x04\x4f\xc1\x45\xf8\x4f\x33\x5c\xb8\x19\x53\xde",
+        "\xfb\x44\x73\x50\xc4\xe8\x68\xc5\x2a\xc3\x27\x5c\xf9\xd4\x32\x7e",
+        0, 32 },
+      { GCRY_MAC_POLY1305_AES,
+        "",
+        "\x75\xde\xaa\x25\xc0\x9f\x20\x8e\x1d\xc4\xce\x6b\x5c\xad\x3f\xbf"
+        "\xa0\xf3\x08\x00\x00\xf4\x64\x00\xd0\xc7\xe9\x07\x6c\x83\x44\x03",
+        "\xdd\x3f\xab\x22\x51\xf1\x1a\xc7\x59\xf0\x88\x71\x29\xcc\x2e\xe7",
+        "\x61\xee\x09\x21\x8d\x29\xb0\xaa\xed\x7e\x15\x4a\x2c\x55\x09\xcc",
+        0, 32 },
+      { GCRY_MAC_POLY1305_AES,
+        "\x66\x3c\xea\x19\x0f\xfb\x83\xd8\x95\x93\xf3\xf4\x76\xb6\xbc\x24"
+        "\xd7\xe6\x79\x10\x7e\xa2\x6a\xdb\x8c\xaf\x66\x52\xd0\x65\x61\x36",
+        "\x6a\xcb\x5f\x61\xa7\x17\x6d\xd3\x20\xc5\xc1\xeb\x2e\xdc\xdc\x74"
+        "\x48\x44\x3d\x0b\xb0\xd2\x11\x09\xc8\x9a\x10\x0b\x5c\xe2\xc2\x08",
+        "\x0e\xe1\xc1\x6b\xb7\x3f\x0f\x4f\xd1\x98\x81\x75\x3c\x01\xcd\xbe",
+        "\xae\x21\x2a\x55\x39\x97\x29\x59\x5d\xea\x45\x8b\xc6\x21\xff\x0e",
+        0, 32 },
+      { GCRY_MAC_POLY1305_AES,
+        "\xab\x08\x12\x72\x4a\x7f\x1e\x34\x27\x42\xcb\xed\x37\x4d\x94\xd1"
+        "\x36\xc6\xb8\x79\x5d\x45\xb3\x81\x98\x30\xf2\xc0\x44\x91\xfa\xf0"
+        "\x99\x0c\x62\xe4\x8b\x80\x18\xb2\xc3\xe4\xa0\xfa\x31\x34\xcb\x67"
+        "\xfa\x83\xe1\x58\xc9\x94\xd9\x61\xc4\xcb\x21\x09\x5c\x1b\xf9",
+        "\xe1\xa5\x66\x8a\x4d\x5b\x66\xa5\xf6\x8c\xc5\x42\x4e\xd5\x98\x2d"
+        "\x12\x97\x6a\x08\xc4\x42\x6d\x0c\xe8\xa8\x24\x07\xc4\xf4\x82\x07",
+        "\x51\x54\xad\x0d\x2c\xb2\x6e\x01\x27\x4f\xc5\x11\x48\x49\x1f\x1b",
+       "\x9a\xe8\x31\xe7\x43\x97\x8d\x3a\x23\x52\x7c\x71\x28\x14\x9e\x3a",
+        0, 32 },
       { 0 },
     };
   int i;
index 3d8ae37..a911ef8 100644 (file)
@@ -1316,6 +1316,19 @@ bench_mac_init (struct bench_obj *obj)
       exit (1);
     }
 
+  switch (mode->algo)
+    {
+    default:
+      break;
+    case GCRY_MAC_POLY1305_AES:
+    case GCRY_MAC_POLY1305_CAMELLIA:
+    case GCRY_MAC_POLY1305_TWOFISH:
+    case GCRY_MAC_POLY1305_SERPENT:
+    case GCRY_MAC_POLY1305_SEED:
+      gcry_mac_setiv (hd, key, 16);
+      break;
+    }
+
   obj->priv = hd;
 
   return 0;
index 9fd716d..042e721 100644 (file)
@@ -509,6 +509,18 @@ mac_bench ( const char *algoname )
   for (i=0; i < bufsize; i++)
     buf[i] = i;
 
+  if (algo >= GCRY_MAC_POLY1305_AES && algo <= GCRY_MAC_POLY1305_SEED)
+    {
+      static const char iv[16] = { 1, 2, 3, 4, };
+      err = gcry_mac_setiv(hd, iv, sizeof(iv));
+      if (err)
+        {
+          fprintf (stderr, PGM ": error setting nonce for mac algorithm `%s': %s\n",
+                   algoname, gpg_strerror (err));
+          exit (1);
+        }
+    }
+
   printf ("%-20s", gcry_mac_algo_name (algo));
 
   start_timer ();