Disallow compiler from generating SSE instructions in mixed C+asm source
[libgcrypt.git] / tests / basic.c
index e406db4..1175b38 100644 (file)
@@ -61,6 +61,22 @@ static int error_count;
 static int in_fips_mode;
 static int die_on_error;
 
+#define MAX_DATA_LEN 128
+
+#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a)                     \
+                      || (*(a) >= 'A' && *(a) <= 'F')  \
+                      || (*(a) >= 'a' && *(a) <= 'f'))
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+#define xmalloc(a)    gcry_xmalloc ((a))
+#define xcalloc(a,b)  gcry_xcalloc ((a),(b))
+#define xstrdup(a)    gcry_xstrdup ((a))
+#define xfree(a)      gcry_free ((a))
+
+
+
 static void
 fail (const char *format, ...)
 {
@@ -74,6 +90,7 @@ fail (const char *format, ...)
     exit (1);
 }
 
+
 static void
 mismatch (const void *expected, size_t expectedlen,
           const void *computed, size_t computedlen)
@@ -102,6 +119,30 @@ die (const char *format, ...)
 }
 
 
+/* Convert STRING consisting of hex characters into its binary
+   representation and return it as an allocated buffer. The valid
+   length of the buffer is returned at R_LENGTH.  The string is
+   delimited by end of string.  The function terminates on error.  */
+static void *
+hex2buffer (const char *string, size_t *r_length)
+{
+  const char *s;
+  unsigned char *buffer;
+  size_t length;
+
+  buffer = xmalloc (strlen(string)/2+1);
+  length = 0;
+  for (s=string; *s; s +=2 )
+    {
+      if (!hexdigitp (s) || !hexdigitp (s+1))
+        die ("invalid hex digits in \"%s\"\n", string);
+      ((unsigned char*)buffer)[length++] = xtoi_2 (s);
+    }
+  *r_length = length;
+  return buffer;
+}
+
+
 static void
 show_sexp (const char *prefix, gcry_sexp_t a)
 {
@@ -195,8 +236,6 @@ show_mac_not_available (int algo)
 
 
 
-#define MAX_DATA_LEN 128
-
 void
 progress_handler (void *cb_data, const char *what, int printchar,
                  int current, int total)
@@ -1625,27 +1664,59 @@ _check_poly1305_cipher (unsigned int step)
   struct tv
   {
     int algo;
-    char key[MAX_DATA_LEN];
-    char iv[MAX_DATA_LEN];
+    const char *key;
+    const char *iv;
     int ivlen;
-    unsigned char aad[MAX_DATA_LEN];
+    const char *aad;
     int aadlen;
-    unsigned char plaintext[MAX_DATA_LEN];
+    const char *plaintext;
     int inlen;
-    char out[MAX_DATA_LEN];
-    char tag[MAX_DATA_LEN];
+    const char *out;
+    const char *tag;
   } tv[] =
     {
-      /* draft-agl-tls-chacha20poly1305-04 */
+      /* draft-irtf-cfrg-chacha20-poly1305-03 */
       { GCRY_CIPHER_CHACHA20,
-        "\x42\x90\xbc\xb1\x54\x17\x35\x31\xf3\x14\xaf\x57\xf3\xbe\x3b\x50"
-       "\x06\xda\x37\x1e\xce\x27\x2a\xfa\x1b\x5d\xbd\xd1\x10\x0a\x10\x07",
-        "\xcd\x7c\xf6\x7b\xe3\x9c\x79\x4a", 8,
-        "\x87\xe2\x29\xd4\x50\x08\x45\xa0\x79\xc0", 10,
-        "\x86\xd0\x99\x74\x84\x0b\xde\xd2\xa5\xca", 10,
-        "\xe3\xe4\x46\xf7\xed\xe9\xa1\x9b\x62\xa4",
-        "\x67\x7d\xab\xf4\xe3\xd2\x4b\x87\x6b\xb2\x84\x75\x38\x96\xe1\xd6" },
-      /* draft-nir-cfrg-chacha20-poly1305-03 */
+       "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a\xf3\x33\x88\x86\x04\xf6\xb5\xf0"
+       "\x47\x39\x17\xc1\x40\x2b\x80\x09\x9d\xca\x5c\xbc\x20\x70\x75\xc0",
+       "\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08", 12,
+       "\xf3\x33\x88\x86\x00\x00\x00\x00\x00\x00\x4e\x91", 12,
+       "\x49\x6e\x74\x65\x72\x6e\x65\x74\x2d\x44\x72\x61\x66\x74\x73\x20"
+       "\x61\x72\x65\x20\x64\x72\x61\x66\x74\x20\x64\x6f\x63\x75\x6d\x65"
+       "\x6e\x74\x73\x20\x76\x61\x6c\x69\x64\x20\x66\x6f\x72\x20\x61\x20"
+       "\x6d\x61\x78\x69\x6d\x75\x6d\x20\x6f\x66\x20\x73\x69\x78\x20\x6d"
+       "\x6f\x6e\x74\x68\x73\x20\x61\x6e\x64\x20\x6d\x61\x79\x20\x62\x65"
+       "\x20\x75\x70\x64\x61\x74\x65\x64\x2c\x20\x72\x65\x70\x6c\x61\x63"
+       "\x65\x64\x2c\x20\x6f\x72\x20\x6f\x62\x73\x6f\x6c\x65\x74\x65\x64"
+       "\x20\x62\x79\x20\x6f\x74\x68\x65\x72\x20\x64\x6f\x63\x75\x6d\x65"
+       "\x6e\x74\x73\x20\x61\x74\x20\x61\x6e\x79\x20\x74\x69\x6d\x65\x2e"
+       "\x20\x49\x74\x20\x69\x73\x20\x69\x6e\x61\x70\x70\x72\x6f\x70\x72"
+       "\x69\x61\x74\x65\x20\x74\x6f\x20\x75\x73\x65\x20\x49\x6e\x74\x65"
+       "\x72\x6e\x65\x74\x2d\x44\x72\x61\x66\x74\x73\x20\x61\x73\x20\x72"
+       "\x65\x66\x65\x72\x65\x6e\x63\x65\x20\x6d\x61\x74\x65\x72\x69\x61"
+       "\x6c\x20\x6f\x72\x20\x74\x6f\x20\x63\x69\x74\x65\x20\x74\x68\x65"
+       "\x6d\x20\x6f\x74\x68\x65\x72\x20\x74\x68\x61\x6e\x20\x61\x73\x20"
+       "\x2f\xe2\x80\x9c\x77\x6f\x72\x6b\x20\x69\x6e\x20\x70\x72\x6f\x67"
+       "\x72\x65\x73\x73\x2e\x2f\xe2\x80\x9d", 265,
+       "\x64\xa0\x86\x15\x75\x86\x1a\xf4\x60\xf0\x62\xc7\x9b\xe6\x43\xbd"
+       "\x5e\x80\x5c\xfd\x34\x5c\xf3\x89\xf1\x08\x67\x0a\xc7\x6c\x8c\xb2"
+       "\x4c\x6c\xfc\x18\x75\x5d\x43\xee\xa0\x9e\xe9\x4e\x38\x2d\x26\xb0"
+       "\xbd\xb7\xb7\x3c\x32\x1b\x01\x00\xd4\xf0\x3b\x7f\x35\x58\x94\xcf"
+       "\x33\x2f\x83\x0e\x71\x0b\x97\xce\x98\xc8\xa8\x4a\xbd\x0b\x94\x81"
+       "\x14\xad\x17\x6e\x00\x8d\x33\xbd\x60\xf9\x82\xb1\xff\x37\xc8\x55"
+       "\x97\x97\xa0\x6e\xf4\xf0\xef\x61\xc1\x86\x32\x4e\x2b\x35\x06\x38"
+       "\x36\x06\x90\x7b\x6a\x7c\x02\xb0\xf9\xf6\x15\x7b\x53\xc8\x67\xe4"
+       "\xb9\x16\x6c\x76\x7b\x80\x4d\x46\xa5\x9b\x52\x16\xcd\xe7\xa4\xe9"
+       "\x90\x40\xc5\xa4\x04\x33\x22\x5e\xe2\x82\xa1\xb0\xa0\x6c\x52\x3e"
+       "\xaf\x45\x34\xd7\xf8\x3f\xa1\x15\x5b\x00\x47\x71\x8c\xbc\x54\x6a"
+       "\x0d\x07\x2b\x04\xb3\x56\x4e\xea\x1b\x42\x22\x73\xf5\x48\x27\x1a"
+       "\x0b\xb2\x31\x60\x53\xfa\x76\x99\x19\x55\xeb\xd6\x31\x59\x43\x4e"
+       "\xce\xbb\x4e\x46\x6d\xae\x5a\x10\x73\xa6\x72\x76\x27\x09\x7a\x10"
+       "\x49\xe6\x17\xd9\x1d\x36\x10\x94\xfa\x68\xf0\xff\x77\x98\x71\x30"
+       "\x30\x5b\xea\xba\x2e\xda\x04\xdf\x99\x7b\x71\x4d\x6c\x6f\x2c\x29"
+       "\xa6\xad\x5c\xb4\x02\x2b\x02\x70\x9b",
+       "\xee\xad\x9d\x67\x89\x0c\xbb\x22\x39\x23\x36\xfe\xa1\x85\x1f\x38" },
+      /* draft-irtf-cfrg-chacha20-poly1305-03 */
       { GCRY_CIPHER_CHACHA20,
        "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
        "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
@@ -1661,11 +1732,11 @@ _check_poly1305_cipher (unsigned int step)
        "\xfa\xb3\x24\xe4\xfa\xd6\x75\x94\x55\x85\x80\x8b\x48\x31\xd7\xbc"
        "\x3f\xf4\xde\xf0\x8e\x4b\x7a\x9d\xe5\x76\xd2\x65\x86\xce\xc6\x4b"
        "\x61\x16",
-       "\x18\xfb\x11\xa5\x03\x1a\xd1\x3a\x7e\x3b\x03\xd4\x6e\xe3\xa6\xa7" }
+       "\x1a\xe1\x0b\x59\x4f\x09\xe2\x6a\x7e\x90\x2e\xcb\xd0\x60\x06\x91" },
     };
 
   gcry_cipher_hd_t hde, hdd;
-  unsigned char out[MAX_DATA_LEN];
+  unsigned char out[1024];
   unsigned char tag[16];
   int i, keylen;
   gcry_error_t err = 0;
@@ -2710,6 +2781,564 @@ check_ccm_cipher (void)
 
 
 static void
+do_check_ocb_cipher (int inplace)
+{
+  /* Note that we use hex strings and not binary strings in TV.  That
+     makes it easier to maintain the test vectors.  */
+  static const struct
+  {
+    int algo;
+    int taglen;         /* 16, 12, or 8 bytes  */
+    const char *key;    /* NULL means "000102030405060708090A0B0C0D0E0F" */
+    const char *nonce;
+    const char *aad;
+    const char *plain;
+    const char *ciph;
+  } tv[] = {
+    /* The RFC-7253 test vectos*/
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221100",
+      "",
+      "",
+      "785407BFFFC8AD9EDCC5520AC9111EE6"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221101",
+      "0001020304050607",
+      "0001020304050607",
+      "6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221102",
+      "0001020304050607",
+      "",
+      "81017F8203F081277152FADE694A0A00"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221103",
+      "",
+      "0001020304050607",
+      "45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221104",
+      "000102030405060708090A0B0C0D0E0F",
+      "000102030405060708090A0B0C0D0E0F",
+      "571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5"
+      "701C1CCEC8FC3358"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221105",
+      "000102030405060708090A0B0C0D0E0F",
+      "",
+      "8CF761B6902EF764462AD86498CA6B97"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221106",
+      "",
+      "000102030405060708090A0B0C0D0E0F",
+      "5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436B"
+      "DF06D8FA1ECA343D"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221107",
+      "000102030405060708090A0B0C0D0E0F1011121314151617",
+      "000102030405060708090A0B0C0D0E0F1011121314151617",
+      "1CA2207308C87C010756104D8840CE1952F09673A448A122"
+      "C92C62241051F57356D7F3C90BB0E07F"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221108",
+      "000102030405060708090A0B0C0D0E0F1011121314151617",
+      "",
+      "6DC225A071FC1B9F7C69F93B0F1E10DE"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA99887766554433221109",
+      "",
+      "000102030405060708090A0B0C0D0E0F1011121314151617",
+      "221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3C"
+      "E725F32494B9F914D85C0B1EB38357FF"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA9988776655443322110A",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F",
+      "BD6F6C496201C69296C11EFD138A467ABD3C707924B964DE"
+      "AFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA9988776655443322110B",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F",
+      "",
+      "FE80690BEE8A485D11F32965BC9D2A32"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA9988776655443322110C",
+      "",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F",
+      "2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF4"
+      "6040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA9988776655443322110D",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F2021222324252627",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F2021222324252627",
+      "D5CA91748410C1751FF8A2F618255B68A0A12E093FF45460"
+      "6E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483"
+      "A7035490C5769E60"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA9988776655443322110E",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F2021222324252627",
+      "",
+      "C5CD9D1850C141E358649994EE701B68"
+    },
+    { GCRY_CIPHER_AES, 16, NULL,
+      "BBAA9988776655443322110F",
+      "",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F2021222324252627",
+      "4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15"
+      "A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95"
+      "A98CA5F3000B1479"
+    },
+    { GCRY_CIPHER_AES, 12, "0F0E0D0C0B0A09080706050403020100",
+      "BBAA9988776655443322110D",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F2021222324252627",
+      "000102030405060708090A0B0C0D0E0F1011121314151617"
+      "18191A1B1C1D1E1F2021222324252627",
+      "1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1"
+      "A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FD"
+      "AC4F02AA"
+    }
+  };
+  gpg_error_t err = 0;
+  gcry_cipher_hd_t hde, hdd;
+  unsigned char out[MAX_DATA_LEN];
+  unsigned char tag[16];
+  int tidx;
+
+  if (verbose)
+    fprintf (stderr, "  Starting OCB checks.\n");
+
+  for (tidx = 0; tidx < DIM (tv); tidx++)
+    {
+      char *key, *nonce, *aad, *ciph, *plain;
+      size_t keylen, noncelen, aadlen, ciphlen, plainlen;
+      int taglen;
+
+      if (verbose)
+        fprintf (stderr, "    checking OCB mode for %s [%i] (tv %d)\n",
+                 gcry_cipher_algo_name (tv[tidx].algo), tv[tidx].algo, tidx);
+
+      /* Convert to hex strings to binary.  */
+      key   = hex2buffer (tv[tidx].key? tv[tidx].key
+                          /*        */: "000102030405060708090A0B0C0D0E0F",
+                          &keylen);
+      nonce = hex2buffer (tv[tidx].nonce, &noncelen);
+      aad   = hex2buffer (tv[tidx].aad, &aadlen);
+      plain = hex2buffer (tv[tidx].plain, &plainlen);
+      ciph  = hex2buffer (tv[tidx].ciph, &ciphlen);
+
+      /* Check that our test vectors are sane.  */
+      assert (plainlen <= sizeof out);
+      assert (tv[tidx].taglen <= ciphlen);
+      assert (tv[tidx].taglen <= sizeof tag);
+
+      err = gcry_cipher_open (&hde, tv[tidx].algo, GCRY_CIPHER_MODE_OCB, 0);
+      if (!err)
+        err = gcry_cipher_open (&hdd, tv[tidx].algo, GCRY_CIPHER_MODE_OCB, 0);
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_open failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          return;
+        }
+
+      /* Set the taglen.  For the first handle we do this only for a
+         non-default taglen.  For the second handle we check that we
+         can also set to the default taglen.  */
+      taglen = tv[tidx].taglen;
+      if (taglen != 16)
+        {
+          err = gcry_cipher_ctl (hde, GCRYCTL_SET_TAGLEN,
+                                 &taglen, sizeof taglen);
+          if (err)
+            {
+              fail ("cipher-ocb, gcryctl_set_taglen failed (tv %d): %s\n",
+                    tidx, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+      err = gcry_cipher_ctl (hdd, GCRYCTL_SET_TAGLEN,
+                             &taglen, sizeof taglen);
+      if (err)
+        {
+          fail ("cipher-ocb, gcryctl_set_taglen failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_setkey (hde, key, keylen);
+      if (!err)
+        err = gcry_cipher_setkey (hdd, key, keylen);
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_setkey failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_setiv (hde, nonce, noncelen);
+      if (!err)
+        err = gcry_cipher_setiv (hdd, nonce, noncelen);
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_setiv failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_authenticate (hde, aad, aadlen);
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_authenticate failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_final (hde);
+      if (!err)
+        {
+          if (inplace)
+            {
+              memcpy(out, plain, plainlen);
+              err = gcry_cipher_encrypt (hde, out, plainlen, NULL, 0);
+            }
+          else
+            {
+              err = gcry_cipher_encrypt (hde, out, MAX_DATA_LEN,
+                                         plain, plainlen);
+            }
+        }
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_encrypt failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* Check that the encrypt output matches the expected cipher
+         text without the tag (i.e. at the length of plaintext).  */
+      if (memcmp (ciph, out, plainlen))
+        {
+          mismatch (ciph, plainlen, out, plainlen);
+          fail ("cipher-ocb, encrypt data mismatch (tv %d)\n", tidx);
+        }
+
+      /* Check that the tag matches TAGLEN bytes from the end of the
+         expected ciphertext.  */
+      err = gcry_cipher_gettag (hde, tag, tv[tidx].taglen);
+      if (err)
+        {
+          fail ("cipher_ocb, gcry_cipher_gettag failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+        }
+      if (memcmp (ciph + ciphlen - tv[tidx].taglen, tag, tv[tidx].taglen))
+        {
+          mismatch (ciph + ciphlen - tv[tidx].taglen, tv[tidx].taglen,
+                    tag, tv[tidx].taglen);
+          fail ("cipher-ocb, encrypt tag mismatch (tv %d)\n", tidx);
+        }
+
+
+      err = gcry_cipher_authenticate (hdd, aad, aadlen);
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_authenticate failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* Now for the decryption.  */
+      err = gcry_cipher_final (hdd);
+      if (!err)
+        {
+          if (inplace)
+            {
+              err = gcry_cipher_decrypt (hdd, out, plainlen, NULL, 0);
+            }
+          else
+            {
+              unsigned char tmp[MAX_DATA_LEN];
+
+              memcpy(tmp, out, plainlen);
+              err = gcry_cipher_decrypt (hdd, out, plainlen, tmp, plainlen);
+            }
+        }
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_decrypt (tv %d) failed: %s\n",
+                tidx, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* We still have TAG from the encryption.  */
+      err = gcry_cipher_checktag (hdd, tag, tv[tidx].taglen);
+      if (err)
+        {
+          fail ("cipher-ocb, gcry_cipher_checktag failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+        }
+
+      /* Check that the decrypt output matches the original plaintext.  */
+      if (memcmp (plain, out, plainlen))
+        {
+          mismatch (plain, plainlen, out, plainlen);
+          fail ("cipher-ocb, decrypt data mismatch (tv %d)\n", tidx);
+        }
+
+      /* Check that gettag also works for decryption.  */
+      err = gcry_cipher_gettag (hdd, tag, tv[tidx].taglen);
+      if (err)
+        {
+          fail ("cipher_ocb, decrypt gettag failed (tv %d): %s\n",
+                tidx, gpg_strerror (err));
+        }
+      if (memcmp (ciph + ciphlen - tv[tidx].taglen, tag, tv[tidx].taglen))
+        {
+          mismatch (ciph + ciphlen - tv[tidx].taglen, tv[tidx].taglen,
+                    tag, tv[tidx].taglen);
+          fail ("cipher-ocb, decrypt tag mismatch (tv %d)\n", tidx);
+        }
+
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+
+      xfree (nonce);
+      xfree (aad);
+      xfree (ciph);
+      xfree (plain);
+      xfree (key);
+    }
+
+  if (verbose)
+    fprintf (stderr, "  Completed OCB checks.\n");
+}
+
+
+static void
+check_ocb_cipher_largebuf (int algo, int keylen, const char *tagexpect)
+{
+  static const unsigned char key[32] =
+        "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+        "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F";
+  static const unsigned char nonce[12] =
+        "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x00\x01\x02\x03";
+  const size_t buflen = 1024 * 1024 * 2 + 32;
+  unsigned char *inbuf;
+  unsigned char *outbuf;
+  gpg_error_t err = 0;
+  gcry_cipher_hd_t hde, hdd;
+  unsigned char tag[16];
+  int i;
+
+  inbuf = xmalloc(buflen);
+  if (!inbuf)
+    {
+      fail ("out-of-memory\n");
+      return;
+    }
+  outbuf = xmalloc(buflen);
+  if (!outbuf)
+    {
+      fail ("out-of-memory\n");
+      xfree(inbuf);
+      return;
+    }
+
+  for (i = 0; i < buflen; i++)
+    inbuf[i] = 'a';
+
+  err = gcry_cipher_open (&hde, algo, GCRY_CIPHER_MODE_OCB, 0);
+  if (!err)
+    err = gcry_cipher_open (&hdd, algo, GCRY_CIPHER_MODE_OCB, 0);
+  if (err)
+    {
+      fail ("cipher-ocb, gcry_cipher_open failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+      goto out_free;
+    }
+
+  err = gcry_cipher_setkey (hde, key, keylen);
+  if (!err)
+    err = gcry_cipher_setkey (hdd, key, keylen);
+  if (err)
+    {
+      fail ("cipher-ocb, gcry_cipher_setkey failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+      goto out_free;
+    }
+
+  err = gcry_cipher_setiv (hde, nonce, 12);
+  if (!err)
+    err = gcry_cipher_setiv (hdd, nonce, 12);
+  if (err)
+    {
+      fail ("cipher-ocb, gcry_cipher_setiv failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+      goto out_free;
+    }
+
+  err = gcry_cipher_authenticate (hde, inbuf, buflen);
+  if (err)
+    {
+      fail ("cipher-ocb, gcry_cipher_authenticate failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+      goto out_free;
+    }
+
+  err = gcry_cipher_final (hde);
+  if (!err)
+    {
+      err = gcry_cipher_encrypt (hde, outbuf, buflen, inbuf, buflen);
+    }
+  if (err)
+    {
+      fail ("cipher-ocb, gcry_cipher_encrypt failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+      goto out_free;
+    }
+
+  /* Check that the tag matches. */
+  err = gcry_cipher_gettag (hde, tag, 16);
+  if (err)
+    {
+      fail ("cipher_ocb, gcry_cipher_gettag failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+    }
+  if (memcmp (tagexpect, tag, 16))
+    {
+      mismatch (tagexpect, 16, tag, 16);
+      fail ("cipher-ocb, encrypt tag mismatch (large, algo %d)\n", algo);
+    }
+
+  err = gcry_cipher_authenticate (hdd, inbuf, buflen);
+  if (err)
+    {
+      fail ("cipher-ocb, gcry_cipher_authenticate failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+      goto out_free;
+    }
+
+  /* Now for the decryption.  */
+  err = gcry_cipher_final (hdd);
+  if (!err)
+    {
+      err = gcry_cipher_decrypt (hdd, outbuf, buflen, NULL, 0);
+    }
+  if (err)
+    {
+      fail ("cipher-ocb, gcry_cipher_decrypt (large, algo %d) failed: %s\n",
+            algo, gpg_strerror (err));
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+      goto out_free;
+    }
+
+  /* We still have TAG from the encryption.  */
+  err = gcry_cipher_checktag (hdd, tag, 16);
+  if (err)
+    {
+      fail ("cipher-ocb, gcry_cipher_checktag failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+    }
+
+  /* Check that the decrypt output matches the original plaintext.  */
+  if (memcmp (inbuf, outbuf, buflen))
+    {
+      /*mismatch (inbuf, buflen, outbuf, buflen);*/
+      fail ("cipher-ocb, decrypt data mismatch (large, algo %d)\n", algo);
+    }
+
+  /* Check that gettag also works for decryption.  */
+  err = gcry_cipher_gettag (hdd, tag, 16);
+  if (err)
+    {
+      fail ("cipher_ocb, decrypt gettag failed (large, algo %d): %s\n",
+            algo, gpg_strerror (err));
+    }
+  if (memcmp (tagexpect, tag, 16))
+    {
+      mismatch (tagexpect, 16, tag, 16);
+      fail ("cipher-ocb, decrypt tag mismatch (large, algo %d)\n", algo);
+    }
+
+  gcry_cipher_close (hde);
+  gcry_cipher_close (hdd);
+
+out_free:
+  xfree(outbuf);
+  xfree(inbuf);
+}
+
+
+static void
+check_ocb_cipher (void)
+{
+  /* Check OCB cipher with separate destination and source buffers for
+   * encryption/decryption. */
+  do_check_ocb_cipher(0);
+
+  /* Check OCB cipher with inplace encrypt/decrypt. */
+  do_check_ocb_cipher(1);
+
+  /* Check large buffer encryption/decryption. */
+  check_ocb_cipher_largebuf(GCRY_CIPHER_AES, 16,
+                            "\xf5\xf3\x12\x7d\x58\x2d\x96\xe8"
+                            "\x33\xfd\x7a\x4f\x42\x60\x5d\x20");
+  check_ocb_cipher_largebuf(GCRY_CIPHER_AES256, 32,
+                            "\xfa\x26\xa5\xbf\xf6\x7d\x3a\x8d"
+                            "\xfe\x96\x67\xc9\xc8\x41\x03\x51");
+}
+
+
+static void
 check_stream_cipher (void)
 {
   static const struct tv
@@ -4333,9 +4962,7 @@ check_ciphers (void)
                 gcry_cipher_algo_name (algos2[i]));
 
       check_one_cipher (algos2[i], GCRY_CIPHER_MODE_STREAM, 0);
-      if (algos2[i] == GCRY_CIPHER_CHACHA20 ||
-         algos2[i] == GCRY_CIPHER_SALSA20 ||
-         algos2[i] == GCRY_CIPHER_SALSA20R12)
+      if (algos2[i] == GCRY_CIPHER_CHACHA20)
        check_one_cipher (algos2[i], GCRY_CIPHER_MODE_POLY1305, 0);
     }
   /* we have now run all cipher's selftests */
@@ -4361,6 +4988,7 @@ check_cipher_modes(void)
   check_ccm_cipher ();
   check_gcm_cipher ();
   check_poly1305_cipher ();
+  check_ocb_cipher ();
   check_stream_cipher ();
   check_stream_cipher_large_block ();
 
@@ -7113,6 +7741,7 @@ main (int argc, char **argv)
   int use_fips = 0;
   int selftest_only = 0;
   int pubkey_only = 0;
+  int cipher_modes_only = 0;
   int loop = 0;
   unsigned int loopcount = 0;
 
@@ -7153,6 +7782,11 @@ main (int argc, char **argv)
           pubkey_only = 1;
           argc--; argv++;
         }
+      else if (!strcmp (*argv, "--cipher-modes"))
+        {
+          cipher_modes_only = 1;
+          argc--; argv++;
+        }
       else if (!strcmp (*argv, "--die"))
         {
           die_on_error = 1;
@@ -7198,6 +7832,8 @@ main (int argc, char **argv)
     {
       if (pubkey_only)
         check_pubkey ();
+      else if (cipher_modes_only)
+        check_ciphers ();
       else if (!selftest_only)
         {
           check_ciphers ();