Add AES-NI acceleration for AES-XTS
[libgcrypt.git] / cipher / cipher.c
index 8483c5f..063c13d 100644 (file)
@@ -26,6 +26,7 @@
 #include <errno.h>
 
 #include "g10lib.h"
+#include "../src/gcrypt-testapi.h"
 #include "cipher.h"
 #include "./cipher-internal.h"
 
@@ -174,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);
@@ -396,19 +399,23 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
     switch (mode)
       {
       case GCRY_CIPHER_MODE_CCM:
-#ifdef HAVE_U64_TYPEDEF
        if (spec->blocksize != GCRY_CCM_BLOCK_LEN)
          err = GPG_ERR_INV_CIPHER_MODE;
        if (!spec->encrypt || !spec->decrypt)
          err = GPG_ERR_INV_CIPHER_MODE;
        break;
-#else
-        err = GPG_ERR_NOT_SUPPORTED;
-#endif
+
+      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:
@@ -469,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
@@ -479,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) )
@@ -512,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
@@ -553,6 +574,8 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
               h->bulk.cbc_dec = _gcry_serpent_cbc_dec;
               h->bulk.cfb_dec = _gcry_serpent_cfb_dec;
               h->bulk.ctr_enc = _gcry_serpent_ctr_enc;
+              h->bulk.ocb_crypt = _gcry_serpent_ocb_crypt;
+              h->bulk.ocb_auth  = _gcry_serpent_ocb_auth;
               break;
 #endif /*USE_SERPENT*/
 #ifdef USE_TWOFISH
@@ -577,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;
             }
@@ -588,7 +618,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
 
   *handle = err ? NULL : h;
 
-  return gcry_error (err);
+  return err;
 }
 
 
@@ -629,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)
     {
@@ -652,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;
         };
@@ -740,11 +801,9 @@ cipher_reset (gcry_cipher_hd_t c)
       memset (&c->u_mode.poly1305, 0, sizeof c->u_mode.poly1305);
       break;
 
-#ifdef HAVE_U64_TYPEDEF
     case GCRY_CIPHER_MODE_CCM:
       memset (&c->u_mode.ccm, 0, sizeof c->u_mode.ccm);
       break;
-#endif
 
     case GCRY_CIPHER_MODE_OCB:
       memset (&c->u_mode.ocb, 0, sizeof c->u_mode.ocb);
@@ -752,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. */
     }
@@ -819,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:
@@ -833,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;
@@ -867,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);
@@ -928,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,
@@ -936,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:
@@ -950,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;
@@ -984,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);
@@ -1106,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,
@@ -1261,7 +1364,6 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
       break;
 
     case GCRYCTL_SET_CCM_LENGTHS:
-#ifdef HAVE_U64_TYPEDEF
       {
         u64 params[3];
         size_t encryptedlen;
@@ -1269,10 +1371,10 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
         size_t authtaglen;
 
         if (h->mode != GCRY_CIPHER_MODE_CCM)
-          return gcry_error (GPG_ERR_INV_CIPHER_MODE);
+          return GPG_ERR_INV_CIPHER_MODE;
 
         if (!buffer || buflen != 3 * sizeof(u64))
-          return gcry_error (GPG_ERR_INV_ARG);
+          return GPG_ERR_INV_ARG;
 
         /* This command is used to pass additional length parameters needed
            by CCM mode to initialize CBC-MAC.  */
@@ -1283,9 +1385,6 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 
         rc = _gcry_cipher_ccm_set_lengths (h, encryptedlen, aadlen, authtaglen);
       }
-#else
-      rc = GPG_ERR_NOT_SUPPORTED;
-#endif
       break;
 
     case GCRYCTL_SET_TAGLEN:
@@ -1315,11 +1414,11 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
       /* This command expects NULL for H and BUFFER to point to an
          integer with the algo number.  */
       if( h || !buffer || buflen != sizeof(int) )
-       return gcry_error (GPG_ERR_CIPHER_ALGO);
+       return GPG_ERR_CIPHER_ALGO;
       disable_cipher_algo( *(int*)buffer );
       break;
 
-    case 61:  /* Disable weak key detection (private).  */
+    case PRIV_CIPHERCTL_DISABLE_WEAK_KEY:  /* (private)  */
       if (h->spec->set_extra_info)
         rc = h->spec->set_extra_info
           (&h->context.c, CIPHER_INFO_NO_WEAK_KEY, NULL, 0);
@@ -1327,7 +1426,7 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
         rc = GPG_ERR_NOT_SUPPORTED;
       break;
 
-    case 62: /* Return current input vector (private).  */
+    case PRIV_CIPHERCTL_GET_INPUT_VECTOR: /* (private)  */
       /* This is the input block as used in CFB and OFB mode which has
          initially been set as IV.  The returned format is:
            1 byte  Actual length of the block in bytes.
@@ -1357,6 +1456,7 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
           (&h->context.c, GCRYCTL_SET_SBOX, buffer, buflen);
       else
         rc = GPG_ERR_NOT_SUPPORTED;
+      break;
 
     default:
       rc = GPG_ERR_INV_OP;
@@ -1367,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;
     }
@@ -1510,6 +1641,17 @@ _gcry_cipher_get_algo_blklen (int algo)
 gcry_err_code_t
 _gcry_cipher_init (void)
 {
+  if (fips_mode())
+    {
+      /* disable algorithms that are disallowed in fips */
+      int idx;
+      gcry_cipher_spec_t *spec;
+
+      for (idx = 0; (spec = cipher_list[idx]); idx++)
+        if (!spec->flags.fips)
+          spec->flags.disabled = 1;
+    }
+
   return 0;
 }