rijndael: refactor to reduce number of #ifdefs and branches
[libgcrypt.git] / cipher / mac-poly1305.c
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