Add AES-NI acceleration for AES-XTS
[libgcrypt.git] / cipher / cipher.c
index 3a8597f..063c13d 100644 (file)
@@ -175,8 +175,10 @@ search_oid (const char *oid, gcry_cipher_oid_spec_t *oid_spec)
   gcry_cipher_spec_t *spec;
   int i;
 
-  if (oid && ((! strncmp (oid, "oid.", 4))
-             || (! strncmp (oid, "OID.", 4))))
+  if (!oid)
+    return NULL;
+
+  if (!strncmp (oid, "oid.", 4) || !strncmp (oid, "OID.", 4))
     oid += 4;
 
   spec = spec_from_oid (oid);
@@ -403,9 +405,17 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
          err = GPG_ERR_INV_CIPHER_MODE;
        break;
 
+      case GCRY_CIPHER_MODE_XTS:
+       if (spec->blocksize != GCRY_XTS_BLOCK_LEN)
+         err = GPG_ERR_INV_CIPHER_MODE;
+       if (!spec->encrypt || !spec->decrypt)
+         err = GPG_ERR_INV_CIPHER_MODE;
+       break;
+
       case GCRY_CIPHER_MODE_ECB:
       case GCRY_CIPHER_MODE_CBC:
       case GCRY_CIPHER_MODE_CFB:
+      case GCRY_CIPHER_MODE_CFB8:
       case GCRY_CIPHER_MODE_OFB:
       case GCRY_CIPHER_MODE_CTR:
       case GCRY_CIPHER_MODE_AESWRAP:
@@ -466,6 +476,18 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
 #endif /*NEED_16BYTE_ALIGNED_CONTEXT*/
                      );
 
+      /* Space needed per mode.  */
+      switch (mode)
+       {
+       case GCRY_CIPHER_MODE_XTS:
+         /* Additional cipher context for tweak. */
+         size += 2 * spec->contextsize + 15;
+         break;
+
+       default:
+         break;
+       }
+
       if (secure)
        h = xtrycalloc_secure (1, size);
       else
@@ -476,6 +498,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
       else
        {
           size_t off = 0;
+         char *tc;
 
 #ifdef NEED_16BYTE_ALIGNED_CONTEXT
           if ( ((uintptr_t)h & 0x0f) )
@@ -509,6 +532,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
               h->bulk.ctr_enc = _gcry_aes_ctr_enc;
               h->bulk.ocb_crypt = _gcry_aes_ocb_crypt;
               h->bulk.ocb_auth  = _gcry_aes_ocb_auth;
+              h->bulk.xts_crypt = _gcry_aes_xts_crypt;
               break;
 #endif /*USE_AES*/
 #ifdef USE_BLOWFISH
@@ -576,6 +600,13 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
               h->u_mode.ocb.taglen = 16; /* Bytes.  */
               break;
 
+           case GCRY_CIPHER_MODE_XTS:
+             tc = h->context.c + spec->contextsize * 2;
+             tc += (16 - (uintptr_t)tc % 16) % 16;
+             h->u_mode.xts.tweak_context = tc;
+
+             break;
+
             default:
               break;
             }
@@ -628,6 +659,23 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
 {
   gcry_err_code_t rc;
 
+  if (c->mode == GCRY_CIPHER_MODE_XTS)
+    {
+      /* XTS uses two keys. */
+      if (keylen % 2)
+       return GPG_ERR_INV_KEYLEN;
+      keylen /= 2;
+
+      if (fips_mode ())
+       {
+         /* Reject key if subkeys Key_1 and Key_2 are equal.
+            See "Implementation Guidance for FIPS 140-2, A.9 XTS-AES
+            Key Generation Requirements" for details.  */
+         if (buf_eq_const (key, key + keylen, keylen))
+           return GPG_ERR_WEAK_KEY;
+       }
+    }
+
   rc = c->spec->setkey (&c->context.c, key, keylen);
   if (!rc)
     {
@@ -651,6 +699,20 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
           _gcry_cipher_poly1305_setkey (c);
           break;
 
+       case GCRY_CIPHER_MODE_XTS:
+         /* Setup tweak cipher with second part of XTS key. */
+         rc = c->spec->setkey (c->u_mode.xts.tweak_context, key + keylen,
+                               keylen);
+         if (!rc)
+           {
+             /* Duplicate initial tweak context.  */
+             memcpy (c->u_mode.xts.tweak_context + c->spec->contextsize,
+                     c->u_mode.xts.tweak_context, c->spec->contextsize);
+           }
+         else
+           c->marks.key = 0;
+         break;
+
         default:
           break;
         };
@@ -749,6 +811,12 @@ cipher_reset (gcry_cipher_hd_t c)
       c->u_mode.ocb.taglen = 16;
       break;
 
+    case GCRY_CIPHER_MODE_XTS:
+      memcpy (c->u_mode.xts.tweak_context,
+             c->u_mode.xts.tweak_context + c->spec->contextsize,
+             c->spec->contextsize);
+      break;
+
     default:
       break; /* u_mode unused by other modes. */
     }
@@ -816,6 +884,12 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
 {
   gcry_err_code_t rc;
 
+  if (c->mode != GCRY_CIPHER_MODE_NONE && !c->marks.key)
+    {
+      log_error ("cipher_encrypt: key not set\n");
+      return GPG_ERR_MISSING_KEY;
+    }
+
   switch (c->mode)
     {
     case GCRY_CIPHER_MODE_ECB:
@@ -830,6 +904,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_cfb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_CFB8:
+      rc = _gcry_cipher_cfb8_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_OFB:
       rc = _gcry_cipher_ofb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
@@ -864,6 +942,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_ocb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_XTS:
+      rc = _gcry_cipher_xts_crypt (c, outbuf, outbuflen, inbuf, inbuflen, 1);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stencrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -925,7 +1007,7 @@ _gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
 /****************
  * Decrypt INBUF to OUTBUF with the mode selected at open.
  * inbuf and outbuf may overlap or be the same.
- * Depending on the mode some some contraints apply to INBUFLEN.
+ * Depending on the mode some some constraints apply to INBUFLEN.
  */
 static gcry_err_code_t
 cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
@@ -933,6 +1015,12 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
 {
   gcry_err_code_t rc;
 
+  if (c->mode != GCRY_CIPHER_MODE_NONE && !c->marks.key)
+    {
+      log_error ("cipher_decrypt: key not set\n");
+      return GPG_ERR_MISSING_KEY;
+    }
+
   switch (c->mode)
     {
     case GCRY_CIPHER_MODE_ECB:
@@ -947,6 +1035,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_cfb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_CFB8:
+      rc = _gcry_cipher_cfb8_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_OFB:
       rc = _gcry_cipher_ofb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
@@ -981,6 +1073,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_ocb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_XTS:
+      rc = _gcry_cipher_xts_crypt (c, outbuf, outbuflen, inbuf, inbuflen, 0);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stdecrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -1103,6 +1199,16 @@ _gcry_cipher_setctr (gcry_cipher_hd_t hd, const void *ctr, size_t ctrlen)
   return 0;
 }
 
+gpg_err_code_t
+_gcry_cipher_getctr (gcry_cipher_hd_t hd, void *ctr, size_t ctrlen)
+{
+  if (ctr && ctrlen == hd->spec->blocksize)
+    memcpy (ctr, hd->u_ctr.ctr, hd->spec->blocksize);
+  else
+    return GPG_ERR_INV_ARG;
+
+  return 0;
+}
 
 gcry_err_code_t
 _gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
@@ -1361,24 +1467,55 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 
 
 /* Return information about the cipher handle H.  CMD is the kind of
-   information requested.  BUFFER and NBYTES are reserved for now.
-
-   There are no values for CMD yet defined.
-
-   The function always returns GPG_ERR_INV_OP.
-
+ * information requested.
+ *
+ * CMD may be one of:
+ *
+ *  GCRYCTL_GET_TAGLEN:
+ *      Return the length of the tag for an AE algorithm mode.  An
+ *      error is returned for modes which do not support a tag.
+ *      BUFFER must be given as NULL.  On success the result is stored
+ *      at NBYTES.  The taglen is returned in bytes.
+ *
+ * The function returns 0 on success or an error code.
  */
 gcry_err_code_t
 _gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
 {
   gcry_err_code_t rc = 0;
 
-  (void)h;
-  (void)buffer;
-  (void)nbytes;
-
   switch (cmd)
     {
+    case GCRYCTL_GET_TAGLEN:
+      if (!h || buffer || !nbytes)
+       rc = GPG_ERR_INV_ARG;
+      else
+       {
+          switch (h->mode)
+            {
+            case GCRY_CIPHER_MODE_OCB:
+              *nbytes = h->u_mode.ocb.taglen;
+              break;
+
+            case GCRY_CIPHER_MODE_CCM:
+              *nbytes = h->u_mode.ccm.authlen;
+              break;
+
+            case GCRY_CIPHER_MODE_GCM:
+              *nbytes = GCRY_GCM_BLOCK_LEN;
+              break;
+
+            case GCRY_CIPHER_MODE_POLY1305:
+              *nbytes = POLY1305_TAGLEN;
+              break;
+
+            default:
+              rc = GPG_ERR_INV_CIPHER_MODE;
+              break;
+            }
+        }
+      break;
+
     default:
       rc = GPG_ERR_INV_OP;
     }