Added API to share precomputations in EAX.
[BearSSL] / test / test_crypto.c
index ddba9ac..46f208c 100644 (file)
@@ -591,7 +591,7 @@ test_HMAC_CT(const br_hash_class *digest_class,
 
        br_hmac_key_init(&kc, digest_class, key, key_len);
 
 
        br_hmac_key_init(&kc, digest_class, key, key_len);
 
-       for (u = 0; u < 130; u ++) {
+       for (u = 0; u < 2; u ++) {
                for (v = 0; v < 130; v ++) {
                        size_t min_len, max_len;
                        size_t w;
                for (v = 0; v < 130; v ++) {
                        size_t min_len, max_len;
                        size_t w;
@@ -1075,21 +1075,43 @@ test_HMAC_DRBG(void)
 }
 
 static void
 }
 
 static void
-do_KAT_PRF(
-       void (*prf)(void *dst, size_t len,
-               const void *secret, size_t secret_len,
-               const char *label, const void *seed, size_t seed_len),
+do_KAT_PRF(br_tls_prf_impl prf,
        const char *ssecret, const char *label, const char *sseed,
        const char *sref)
 {
        unsigned char secret[100], seed[100], ref[500], out[500];
        size_t secret_len, seed_len, ref_len;
        const char *ssecret, const char *label, const char *sseed,
        const char *sref)
 {
        unsigned char secret[100], seed[100], ref[500], out[500];
        size_t secret_len, seed_len, ref_len;
+       br_tls_prf_seed_chunk chunks[2];
 
        secret_len = hextobin(secret, ssecret);
        seed_len = hextobin(seed, sseed);
        ref_len = hextobin(ref, sref);
 
        secret_len = hextobin(secret, ssecret);
        seed_len = hextobin(seed, sseed);
        ref_len = hextobin(ref, sref);
-       prf(out, ref_len, secret, secret_len, label, seed, seed_len);
-       check_equals("TLS PRF KAT", out, ref, ref_len);
+
+       chunks[0].data = seed;
+       chunks[0].len = seed_len;
+       prf(out, ref_len, secret, secret_len, label, 1, chunks);
+       check_equals("TLS PRF KAT 1", out, ref, ref_len);
+
+       chunks[0].data = seed;
+       chunks[0].len = seed_len;
+       chunks[1].data = NULL;
+       chunks[1].len = 0;
+       prf(out, ref_len, secret, secret_len, label, 2, chunks);
+       check_equals("TLS PRF KAT 2", out, ref, ref_len);
+
+       chunks[0].data = NULL;
+       chunks[0].len = 0;
+       chunks[1].data = seed;
+       chunks[1].len = seed_len;
+       prf(out, ref_len, secret, secret_len, label, 2, chunks);
+       check_equals("TLS PRF KAT 3", out, ref, ref_len);
+
+       chunks[0].data = seed;
+       chunks[0].len = seed_len >> 1;
+       chunks[1].data = seed + chunks[0].len;
+       chunks[1].len = seed_len - chunks[0].len;
+       prf(out, ref_len, secret, secret_len, label, 2, chunks);
+       check_equals("TLS PRF KAT 4", out, ref, ref_len);
 }
 
 static void
 }
 
 static void
@@ -3133,6 +3155,71 @@ test_AES_generic(char *name,
                        check_equals("KAT CBC AES decrypt (2)",
                                buf, plain, data_len);
                }
                        check_equals("KAT CBC AES decrypt (2)",
                                buf, plain, data_len);
                }
+
+               /*
+                * We want to check proper IV management for CBC:
+                * encryption and decryption must properly copy the _last_
+                * encrypted block as new IV, for all sizes.
+                */
+               for (u = 1; u <= 35; u ++) {
+                       br_hmac_drbg_context rng;
+                       unsigned char x;
+                       size_t key_len, data_len;
+                       size_t v;
+
+                       br_hmac_drbg_init(&rng, &br_sha256_vtable,
+                               "seed for AES/CBC", 16);
+                       x = u;
+                       br_hmac_drbg_update(&rng, &x, 1);
+                       data_len = u << 4;
+                       for (key_len = 16; key_len <= 32; key_len += 16) {
+                               unsigned char key[32];
+                               unsigned char iv[16], iv1[16], iv2[16];
+                               unsigned char plain[35 * 16];
+                               unsigned char tmp1[sizeof plain];
+                               unsigned char tmp2[sizeof plain];
+                               br_aes_gen_cbcenc_keys v_ec;
+                               br_aes_gen_cbcdec_keys v_dc;
+                               const br_block_cbcenc_class **ec;
+                               const br_block_cbcdec_class **dc;
+
+                               br_hmac_drbg_generate(&rng, key, key_len);
+                               br_hmac_drbg_generate(&rng, iv, sizeof iv);
+                               br_hmac_drbg_generate(&rng, plain, data_len);
+
+                               ec = &v_ec.vtable;
+                               ve->init(ec, key, key_len);
+                               memcpy(iv1, iv, sizeof iv);
+                               memcpy(tmp1, plain, data_len);
+                               ve->run(ec, iv1, tmp1, data_len);
+                               check_equals("IV CBC AES (1)",
+                                       tmp1 + data_len - 16, iv1, 16);
+                               memcpy(iv2, iv, sizeof iv);
+                               memcpy(tmp2, plain, data_len);
+                               for (v = 0; v < data_len; v += 16) {
+                                       ve->run(ec, iv2, tmp2 + v, 16);
+                               }
+                               check_equals("IV CBC AES (2)",
+                                       tmp2 + data_len - 16, iv2, 16);
+                               check_equals("IV CBC AES (3)",
+                                       tmp1, tmp2, data_len);
+
+                               dc = &v_dc.vtable;
+                               vd->init(dc, key, key_len);
+                               memcpy(iv1, iv, sizeof iv);
+                               vd->run(dc, iv1, tmp1, data_len);
+                               check_equals("IV CBC AES (4)", iv1, iv2, 16);
+                               check_equals("IV CBC AES (5)",
+                                       tmp1, plain, data_len);
+                               memcpy(iv2, iv, sizeof iv);
+                               for (v = 0; v < data_len; v += 16) {
+                                       vd->run(dc, iv2, tmp2 + v, 16);
+                               }
+                               check_equals("IV CBC AES (6)", iv1, iv2, 16);
+                               check_equals("IV CBC AES (7)",
+                                       tmp2, plain, data_len);
+                       }
+               }
        }
 
        if (vc != NULL) {
        }
 
        if (vc != NULL) {
@@ -3157,7 +3244,6 @@ test_AES_generic(char *name,
                        data_len = hextobin(plain, KAT_AES_CTR[u + 2]);
                        hextobin(cipher, KAT_AES_CTR[u + 3]);
                        vc->init(xc, key, key_len);
                        data_len = hextobin(plain, KAT_AES_CTR[u + 2]);
                        hextobin(cipher, KAT_AES_CTR[u + 3]);
                        vc->init(xc, key, key_len);
-
                        memcpy(buf, plain, data_len);
                        vc->run(xc, iv, 1, buf, data_len);
                        check_equals("KAT CTR AES (1)", buf, cipher, data_len);
                        memcpy(buf, plain, data_len);
                        vc->run(xc, iv, 1, buf, data_len);
                        check_equals("KAT CTR AES (1)", buf, cipher, data_len);
@@ -3271,6 +3357,278 @@ test_AES_ct64(void)
                1, 1);
 }
 
                1, 1);
 }
 
+static void
+test_AES_x86ni(void)
+{
+       const br_block_cbcenc_class *x_cbcenc;
+       const br_block_cbcdec_class *x_cbcdec;
+       const br_block_ctr_class *x_ctr;
+       int hcbcenc, hcbcdec, hctr;
+
+       x_cbcenc = br_aes_x86ni_cbcenc_get_vtable();
+       x_cbcdec = br_aes_x86ni_cbcdec_get_vtable();
+       x_ctr = br_aes_x86ni_ctr_get_vtable();
+       hcbcenc = (x_cbcenc != NULL);
+       hcbcdec = (x_cbcdec != NULL);
+       hctr = (x_ctr != NULL);
+       if (hcbcenc != hctr || hcbcdec != hctr) {
+               fprintf(stderr, "AES_x86ni availability mismatch (%d/%d/%d)\n",
+                       hcbcenc, hcbcdec, hctr);
+               exit(EXIT_FAILURE);
+       }
+       if (hctr) {
+               test_AES_generic("AES_x86ni",
+                       x_cbcenc, x_cbcdec, x_ctr, 1, 1);
+       } else {
+               printf("Test AES_x86ni: UNAVAILABLE\n");
+       }
+}
+
+static void
+test_AES_pwr8(void)
+{
+       const br_block_cbcenc_class *x_cbcenc;
+       const br_block_cbcdec_class *x_cbcdec;
+       const br_block_ctr_class *x_ctr;
+       int hcbcenc, hcbcdec, hctr;
+
+       x_cbcenc = br_aes_pwr8_cbcenc_get_vtable();
+       x_cbcdec = br_aes_pwr8_cbcdec_get_vtable();
+       x_ctr = br_aes_pwr8_ctr_get_vtable();
+       hcbcenc = (x_cbcenc != NULL);
+       hcbcdec = (x_cbcdec != NULL);
+       hctr = (x_ctr != NULL);
+       if (hcbcenc != hctr || hcbcdec != hctr) {
+               fprintf(stderr, "AES_pwr8 availability mismatch (%d/%d/%d)\n",
+                       hcbcenc, hcbcdec, hctr);
+               exit(EXIT_FAILURE);
+       }
+       if (hctr) {
+               test_AES_generic("AES_pwr8",
+                       x_cbcenc, x_cbcdec, x_ctr, 1, 1);
+       } else {
+               printf("Test AES_pwr8: UNAVAILABLE\n");
+       }
+}
+
+/*
+ * Custom CTR + CBC-MAC AES implementation. Can also do CTR-only, and
+ * CBC-MAC-only. The 'aes_big' implementation (CTR) is used. This is
+ * meant for comparisons.
+ *
+ * If 'ctr' is NULL then no encryption/decryption is done; otherwise,
+ * CTR encryption/decryption is performed (full-block counter) and the
+ * 'ctr' array is updated with the new counter value.
+ *
+ * If 'cbcmac' is NULL then no CBC-MAC is done; otherwise, CBC-MAC is
+ * applied on the encrypted data, with 'cbcmac' as IV and destination
+ * buffer for the output. If 'ctr' is not NULL and 'encrypt' is non-zero,
+ * then CBC-MAC is computed over the result of CTR processing; otherwise,
+ * CBC-MAC is computed over the input data itself.
+ */
+static void
+do_aes_ctrcbc(const void *key, size_t key_len, int encrypt,
+       void *ctr, void *cbcmac, unsigned char *data, size_t len)
+{
+       br_aes_big_ctr_keys bc;
+       int i;
+
+       br_aes_big_ctr_init(&bc, key, key_len);
+       for (i = 0; i < 2; i ++) {
+               /*
+                * CBC-MAC is computed on the encrypted data, so in
+                * first pass if decrypting, second pass if encrypting.
+                */
+               if (cbcmac != NULL
+                       && ((encrypt && i == 1) || (!encrypt && i == 0)))
+               {
+                       unsigned char zz[16];
+                       size_t u;
+
+                       memcpy(zz, cbcmac, sizeof zz);
+                       for (u = 0; u < len; u += 16) {
+                               unsigned char tmp[16];
+                               size_t v;
+
+                               for (v = 0; v < 16; v ++) {
+                                       tmp[v] = zz[v] ^ data[u + v];
+                               }
+                               memset(zz, 0, sizeof zz);
+                               br_aes_big_ctr_run(&bc,
+                                       tmp, br_dec32be(tmp + 12), zz, 16);
+                       }
+                       memcpy(cbcmac, zz, sizeof zz);
+               }
+
+               /*
+                * CTR encryption/decryption is done only in the first pass.
+                * We process data block per block, because the CTR-only
+                * class uses a 32-bit counter, while the CTR+CBC-MAC
+                * class uses a 128-bit counter.
+                */
+               if (ctr != NULL && i == 0) {
+                       unsigned char zz[16];
+                       size_t u;
+
+                       memcpy(zz, ctr, sizeof zz);
+                       for (u = 0; u < len; u += 16) {
+                               int i;
+
+                               br_aes_big_ctr_run(&bc,
+                                       zz, br_dec32be(zz + 12), data + u, 16);
+                               for (i = 15; i >= 0; i --) {
+                                       zz[i] = (zz[i] + 1) & 0xFF;
+                                       if (zz[i] != 0) {
+                                               break;
+                                       }
+                               }
+                       }
+                       memcpy(ctr, zz, sizeof zz);
+               }
+       }
+}
+
+static void
+test_AES_CTRCBC_inner(const char *name, const br_block_ctrcbc_class *vt)
+{
+       br_hmac_drbg_context rng;
+       size_t key_len;
+
+       printf("Test AES CTR/CBC-MAC %s: ", name);
+       fflush(stdout);
+
+       br_hmac_drbg_init(&rng, &br_sha256_vtable, name, strlen(name));
+       for (key_len = 16; key_len <= 32; key_len += 8) {
+               br_aes_gen_ctrcbc_keys bc;
+               unsigned char key[32];
+               size_t data_len;
+
+               br_hmac_drbg_generate(&rng, key, key_len);
+               vt->init(&bc.vtable, key, key_len);
+               for (data_len = 0; data_len <= 512; data_len += 16) {
+                       unsigned char plain[512];
+                       unsigned char data1[sizeof plain];
+                       unsigned char data2[sizeof plain];
+                       unsigned char ctr[16], cbcmac[16];
+                       unsigned char ctr1[16], cbcmac1[16];
+                       unsigned char ctr2[16], cbcmac2[16];
+                       int i;
+
+                       br_hmac_drbg_generate(&rng, plain, data_len);
+
+                       for (i = 0; i <= 16; i ++) {
+                               if (i == 0) {
+                                       br_hmac_drbg_generate(&rng, ctr, 16);
+                               } else {
+                                       memset(ctr, 0, i - 1);
+                                       memset(ctr + i - 1, 0xFF, 17 - i);
+                               }
+                               br_hmac_drbg_generate(&rng, cbcmac, 16);
+
+                               memcpy(data1, plain, data_len);
+                               memcpy(ctr1, ctr, 16);
+                               vt->ctr(&bc.vtable, ctr1, data1, data_len);
+                               memcpy(data2, plain, data_len);
+                               memcpy(ctr2, ctr, 16);
+                               do_aes_ctrcbc(key, key_len, 1,
+                                       ctr2, NULL, data2, data_len);
+                               check_equals("CTR-only data",
+                                       data1, data2, data_len);
+                               check_equals("CTR-only counter",
+                                       ctr1, ctr2, 16);
+
+                               memcpy(data1, plain, data_len);
+                               memcpy(cbcmac1, cbcmac, 16);
+                               vt->mac(&bc.vtable, cbcmac1, data1, data_len);
+                               memcpy(data2, plain, data_len);
+                               memcpy(cbcmac2, cbcmac, 16);
+                               do_aes_ctrcbc(key, key_len, 1,
+                                       NULL, cbcmac2, data2, data_len);
+                               check_equals("CBC-MAC-only",
+                                       cbcmac1, cbcmac2, 16);
+
+                               memcpy(data1, plain, data_len);
+                               memcpy(ctr1, ctr, 16);
+                               memcpy(cbcmac1, cbcmac, 16);
+                               vt->encrypt(&bc.vtable,
+                                       ctr1, cbcmac1, data1, data_len);
+                               memcpy(data2, plain, data_len);
+                               memcpy(ctr2, ctr, 16);
+                               memcpy(cbcmac2, cbcmac, 16);
+                               do_aes_ctrcbc(key, key_len, 1,
+                                       ctr2, cbcmac2, data2, data_len);
+                               check_equals("encrypt: combined data",
+                                       data1, data2, data_len);
+                               check_equals("encrypt: combined counter",
+                                       ctr1, ctr2, 16);
+                               check_equals("encrypt: combined CBC-MAC",
+                                       cbcmac1, cbcmac2, 16);
+
+                               memcpy(ctr1, ctr, 16);
+                               memcpy(cbcmac1, cbcmac, 16);
+                               vt->decrypt(&bc.vtable,
+                                       ctr1, cbcmac1, data1, data_len);
+                               memcpy(ctr2, ctr, 16);
+                               memcpy(cbcmac2, cbcmac, 16);
+                               do_aes_ctrcbc(key, key_len, 0,
+                                       ctr2, cbcmac2, data2, data_len);
+                               check_equals("decrypt: combined data",
+                                       data1, data2, data_len);
+                               check_equals("decrypt: combined counter",
+                                       ctr1, ctr2, 16);
+                               check_equals("decrypt: combined CBC-MAC",
+                                       cbcmac1, cbcmac2, 16);
+                       }
+
+                       printf(".");
+                       fflush(stdout);
+               }
+
+               printf(" ");
+               fflush(stdout);
+       }
+
+       printf("done.\n");
+       fflush(stdout);
+}
+
+static void
+test_AES_CTRCBC_big(void)
+{
+       test_AES_CTRCBC_inner("big", &br_aes_big_ctrcbc_vtable);
+}
+
+static void
+test_AES_CTRCBC_small(void)
+{
+       test_AES_CTRCBC_inner("small", &br_aes_small_ctrcbc_vtable);
+}
+
+static void
+test_AES_CTRCBC_ct(void)
+{
+       test_AES_CTRCBC_inner("ct", &br_aes_ct_ctrcbc_vtable);
+}
+
+static void
+test_AES_CTRCBC_ct64(void)
+{
+       test_AES_CTRCBC_inner("ct64", &br_aes_ct64_ctrcbc_vtable);
+}
+
+static void
+test_AES_CTRCBC_x86ni(void)
+{
+       const br_block_ctrcbc_class *vt;
+
+       vt = br_aes_x86ni_ctrcbc_get_vtable();
+       if (vt != NULL) {
+               test_AES_CTRCBC_inner("x86ni", vt);
+       } else {
+               printf("Test AES CTR/CBC-MAC x86ni: UNAVAILABLE\n");
+       }
+}
+
 /*
  * DES known-answer tests. Order: plaintext, key, ciphertext.
  * (mostly from NIST SP 800-20).
 /*
  * DES known-answer tests. Order: plaintext, key, ciphertext.
  * (mostly from NIST SP 800-20).
@@ -4038,12 +4396,16 @@ static const struct {
 };
 
 static void
 };
 
 static void
-test_ChaCha20_ct(void)
+test_ChaCha20_generic(const char *name, br_chacha20_run cr)
 {
        size_t u;
 
 {
        size_t u;
 
-       printf("Test ChaCha20_ct: ");
+       printf("Test %s: ", name);
        fflush(stdout);
        fflush(stdout);
+       if (cr == 0) {
+               printf("UNAVAILABLE\n");
+               return;
+       }
 
        for (u = 0; KAT_CHACHA20[u].skey; u ++) {
                unsigned char key[32], nonce[12], plain[400], cipher[400];
 
        for (u = 0; KAT_CHACHA20[u].skey; u ++) {
                unsigned char key[32], nonce[12], plain[400], cipher[400];
@@ -4059,10 +4421,11 @@ test_ChaCha20_ct(void)
                for (v = 0; v < len; v ++) {
                        unsigned char tmp[400];
                        size_t w;
                for (v = 0; v < len; v ++) {
                        unsigned char tmp[400];
                        size_t w;
+                       uint32_t cc2;
 
                        memset(tmp, 0, sizeof tmp);
                        memcpy(tmp, plain, v);
 
                        memset(tmp, 0, sizeof tmp);
                        memcpy(tmp, plain, v);
-                       if (br_chacha20_ct_run(key, nonce, cc, tmp, v)
+                       if (cr(key, nonce, cc, tmp, v)
                                != cc + (uint32_t)((v + 63) >> 6))
                        {
                                fprintf(stderr, "ChaCha20: wrong counter\n");
                                != cc + (uint32_t)((v + 63) >> 6))
                        {
                                fprintf(stderr, "ChaCha20: wrong counter\n");
@@ -4078,7 +4441,21 @@ test_ChaCha20_ct(void)
                                        exit(EXIT_FAILURE);
                                }
                        }
                                        exit(EXIT_FAILURE);
                                }
                        }
-                       br_chacha20_ct_run(key, nonce, cc, tmp, v);
+                       for (w = 0, cc2 = cc; w < v; w += 64, cc2 ++) {
+                               size_t x;
+
+                               x = v - w;
+                               if (x > 64) {
+                                       x = 64;
+                               }
+                               if (cr(key, nonce, cc2, tmp + w, x)
+                                       != (cc2 + 1))
+                               {
+                                       fprintf(stderr, "ChaCha20:"
+                                               " wrong counter (2)\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                       }
                        if (memcmp(tmp, plain, v) != 0) {
                                fprintf(stderr, "ChaCha20 KAT fail (2)\n");
                                exit(EXIT_FAILURE);
                        if (memcmp(tmp, plain, v) != 0) {
                                fprintf(stderr, "ChaCha20 KAT fail (2)\n");
                                exit(EXIT_FAILURE);
@@ -4093,6 +4470,18 @@ test_ChaCha20_ct(void)
        fflush(stdout);
 }
 
        fflush(stdout);
 }
 
+static void
+test_ChaCha20_ct(void)
+{
+       test_ChaCha20_generic("ChaCha20_ct", &br_chacha20_ct_run);
+}
+
+static void
+test_ChaCha20_sse2(void)
+{
+       test_ChaCha20_generic("ChaCha20_sse2", br_chacha20_sse2_get());
+}
+
 static const struct {
        const char *splain;
        const char *saad;
 static const struct {
        const char *splain;
        const char *saad;
@@ -4137,24 +4526,12 @@ test_Poly1305_inner(const char *name, br_poly1305_run ipoly,
                memcpy(data, plain, len);
                ipoly(key, nonce, data, len,
                        aad, aad_len, tmp, br_chacha20_ct_run, 1);
                memcpy(data, plain, len);
                ipoly(key, nonce, data, len,
                        aad, aad_len, tmp, br_chacha20_ct_run, 1);
-               if (memcmp(data, cipher, len) != 0) {
-                       fprintf(stderr, "ChaCha20+Poly1305 KAT failed (1)\n");
-                       exit(EXIT_FAILURE);
-               }
-               if (memcmp(tmp, tag, 16) != 0) {
-                       fprintf(stderr, "ChaCha20+Poly1305 KAT failed (2)\n");
-                       exit(EXIT_FAILURE);
-               }
+               check_equals("ChaCha20+Poly1305 KAT (1)", data, cipher, len);
+               check_equals("ChaCha20+Poly1305 KAT (2)", tmp, tag, 16);
                ipoly(key, nonce, data, len,
                        aad, aad_len, tmp, br_chacha20_ct_run, 0);
                ipoly(key, nonce, data, len,
                        aad, aad_len, tmp, br_chacha20_ct_run, 0);
-               if (memcmp(data, plain, len) != 0) {
-                       fprintf(stderr, "ChaCha20+Poly1305 KAT failed (3)\n");
-                       exit(EXIT_FAILURE);
-               }
-               if (memcmp(tmp, tag, 16) != 0) {
-                       fprintf(stderr, "ChaCha20+Poly1305 KAT failed (4)\n");
-                       exit(EXIT_FAILURE);
-               }
+               check_equals("ChaCha20+Poly1305 KAT (3)", data, plain, len);
+               check_equals("ChaCha20+Poly1305 KAT (4)", tmp, tag, 16);
 
                printf(".");
                fflush(stdout);
 
                printf(".");
                fflush(stdout);
@@ -4220,6 +4597,20 @@ test_Poly1305_i15(void)
                &br_poly1305_ctmul_run);
 }
 
                &br_poly1305_ctmul_run);
 }
 
+static void
+test_Poly1305_ctmulq(void)
+{
+       br_poly1305_run bp;
+
+       bp = br_poly1305_ctmulq_get();
+       if (bp == 0) {
+               printf("Test Poly1305_ctmulq: UNAVAILABLE\n");
+       } else {
+               test_Poly1305_inner("Poly1305_ctmulq", bp,
+                       &br_poly1305_ctmul_run);
+       }
+}
+
 /*
  * A 1024-bit RSA key, generated with OpenSSL.
  */
 /*
  * A 1024-bit RSA key, generated with OpenSSL.
  */
@@ -4449,6 +4840,34 @@ test_RSA_i32(void)
                &br_rsa_i32_pkcs1_sign, &br_rsa_i32_pkcs1_vrfy);
 }
 
                &br_rsa_i32_pkcs1_sign, &br_rsa_i32_pkcs1_vrfy);
 }
 
+static void
+test_RSA_i62(void)
+{
+       br_rsa_public pub;
+       br_rsa_private priv;
+       br_rsa_pkcs1_sign sign;
+       br_rsa_pkcs1_vrfy vrfy;
+
+       pub = br_rsa_i62_public_get();
+       priv = br_rsa_i62_private_get();
+       sign = br_rsa_i62_pkcs1_sign_get();
+       vrfy = br_rsa_i62_pkcs1_vrfy_get();
+       if (pub) {
+               if (!priv || !sign || !vrfy) {
+                       fprintf(stderr, "Inconsistent i62 availability\n");
+                       exit(EXIT_FAILURE);
+               }
+               test_RSA_core("RSA i62 core", pub, priv);
+               test_RSA_sign("RSA i62 sign", priv, sign, vrfy);
+       } else {
+               if (priv || sign || vrfy) {
+                       fprintf(stderr, "Inconsistent i62 availability\n");
+                       exit(EXIT_FAILURE);
+               }
+               printf("Test RSA i62: UNAVAILABLE\n");
+       }
+}
+
 #if 0
 static void
 test_RSA_signatures(void)
 #if 0
 static void
 test_RSA_signatures(void)
@@ -4649,6 +5068,31 @@ test_GHASH(const char *name, br_ghash gh)
                check_equals("KAT GHASH", y, ref, sizeof ref);
        }
 
                check_equals("KAT GHASH", y, ref, sizeof ref);
        }
 
+       for (u = 0; u <= 1024; u ++) {
+               unsigned char key[32], iv[12];
+               unsigned char buf[1024 + 32];
+               unsigned char y0[16], y1[16];
+               char tmp[100];
+
+               memset(key, 0, sizeof key);
+               memset(iv, 0, sizeof iv);
+               br_enc32be(key, u);
+               memset(buf, 0, sizeof buf);
+               br_chacha20_ct_run(key, iv, 1, buf, sizeof buf);
+
+               memcpy(y0, buf, 16);
+               br_ghash_ctmul32(y0, buf + 16, buf + 32, u);
+               memcpy(y1, buf, 16);
+               gh(y1, buf + 16, buf + 32, u);
+               sprintf(tmp, "XREF %s (len = %u)", name, (unsigned)u);
+               check_equals(tmp, y0, y1, 16);
+
+               if ((u & 31) == 0) {
+                       printf(".");
+                       fflush(stdout);
+               }
+       }
+
        printf("done.\n");
        fflush(stdout);
 }
        printf("done.\n");
        fflush(stdout);
 }
@@ -4672,106 +5116,934 @@ test_GHASH_ctmul64(void)
 }
 
 static void
 }
 
 static void
-test_EC_inner(const char *sk, const char *sU,
-       const br_ec_impl *impl, int curve)
+test_GHASH_pclmul(void)
 {
 {
-       unsigned char bk[70];
-       unsigned char eG[150], eU[150];
-       uint32_t n[22], n0i;
-       size_t klen, ulen, nlen;
-       const br_ec_curve_def *cd;
-       br_hmac_drbg_context rng;
-       int i;
+       br_ghash gh;
 
 
-       klen = hextobin(bk, sk);
-       ulen = hextobin(eU, sU);
-       switch (curve) {
-       case BR_EC_secp256r1:
-               cd = &br_secp256r1;
-               break;
-       case BR_EC_secp384r1:
-               cd = &br_secp384r1;
-               break;
-       case BR_EC_secp521r1:
-               cd = &br_secp521r1;
-               break;
-       default:
-               fprintf(stderr, "Unknown curve: %d\n", curve);
-               exit(EXIT_FAILURE);
-               break;
-       }
-       if (ulen != cd->generator_len) {
-               fprintf(stderr, "KAT vector wrong (%lu / %lu)\n",
-                       (unsigned long)ulen,
-                       (unsigned long)cd->generator_len);
-       }
-       memcpy(eG, cd->generator, ulen);
-       if (impl->mul(eG, ulen, bk, klen, curve) != 1) {
-               fprintf(stderr, "KAT multiplication failed\n");
-               exit(EXIT_FAILURE);
-       }
-       if (memcmp(eG, eU, ulen) != 0) {
-               fprintf(stderr, "KAT mul: mismatch\n");
-               exit(EXIT_FAILURE);
+       gh = br_ghash_pclmul_get();
+       if (gh == 0) {
+               printf("Test GHASH_pclmul: UNAVAILABLE\n");
+       } else {
+               test_GHASH("GHASH_pclmul", gh);
        }
        }
+}
 
 
-       /*
-        * Test the two-point-mul function. We want to test the basic
-        * functionality, and the following special cases:
-        *   x = y
-        *   x + y = curve order
-        */
-       nlen = cd->order_len;
-       br_i31_decode(n, cd->order, nlen);
-       n0i = br_i31_ninv31(n[1]);
-       br_hmac_drbg_init(&rng, &br_sha256_vtable, "seed for EC", 11);
-       for (i = 0; i < 10; i ++) {
-               unsigned char ba[80], bb[80], bx[80], by[80], bz[80];
-               uint32_t a[22], b[22], x[22], y[22], z[22], t1[22], t2[22];
-               uint32_t r;
-               unsigned char eA[160], eB[160], eC[160], eD[160];
+static void
+test_GHASH_pwr8(void)
+{
+       br_ghash gh;
 
 
-               /*
-                * Generate random a and b, and compute A = a*G and B = b*G.
-                */
-               br_hmac_drbg_generate(&rng, ba, sizeof ba);
-               br_i31_decode_reduce(a, ba, sizeof ba, n);
-               br_i31_encode(ba, nlen, a);
-               br_hmac_drbg_generate(&rng, bb, sizeof bb);
-               br_i31_decode_reduce(b, bb, sizeof bb, n);
-               br_i31_encode(bb, nlen, b);
-               memcpy(eA, cd->generator, ulen);
-               impl->mul(eA, ulen, ba, nlen, cd->curve);
-               memcpy(eB, cd->generator, ulen);
-               impl->mul(eB, ulen, bb, nlen, cd->curve);
+       gh = br_ghash_pwr8_get();
+       if (gh == 0) {
+               printf("Test GHASH_pwr8: UNAVAILABLE\n");
+       } else {
+               test_GHASH("GHASH_pwr8", gh);
+       }
+}
 
 
-               /*
-                * Generate random x and y (modulo n).
-                */
-               br_hmac_drbg_generate(&rng, bx, sizeof bx);
-               br_i31_decode_reduce(x, bx, sizeof bx, n);
-               br_i31_encode(bx, nlen, x);
-               br_hmac_drbg_generate(&rng, by, sizeof by);
-               br_i31_decode_reduce(y, by, sizeof by, n);
-               br_i31_encode(by, nlen, y);
+/*
+ * From: http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
+ *
+ * Order: key, plaintext, AAD, IV, ciphertext, tag
+ */
+static const char *const KAT_GCM[] = {
+       "00000000000000000000000000000000",
+       "",
+       "",
+       "000000000000000000000000",
+       "",
+       "58e2fccefa7e3061367f1d57a4e7455a",
 
 
-               /*
-                * Compute z = a*x + b*y (mod n).
-                */
-               memcpy(t1, x, sizeof x);
-               br_i31_to_monty(t1, n);
-               br_i31_montymul(z, a, t1, n, n0i);
-               memcpy(t1, y, sizeof y);
-               br_i31_to_monty(t1, n);
-               br_i31_montymul(t2, b, t1, n, n0i);
-               r = br_i31_add(z, t2, 1);
-               r |= br_i31_sub(z, n, 0) ^ 1;
-               br_i31_sub(z, n, r);
-               br_i31_encode(bz, nlen, z);
+       "00000000000000000000000000000000",
+       "00000000000000000000000000000000",
+       "",
+       "000000000000000000000000",
+       "0388dace60b6a392f328c2b971b2fe78",
+       "ab6e47d42cec13bdf53a67b21257bddf",
 
 
-               /*
-                * Compute C = x*A + y*B with muladd(), and also
-                * D = z*G with mul(). The two points must match.
+       "feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+       "",
+       "cafebabefacedbaddecaf888",
+       "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
+       "4d5c2af327cd64a62cf35abd2ba6fab4",
+
+       "feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbaddecaf888",
+       "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
+       "5bc94fbc3221a5db94fae95ae7121a47",
+
+       "feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbad",
+       "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
+       "3612d2e79e3b0785561be14aaca2fccb",
+
+       "feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+       "8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
+       "619cc5aefffe0bfa462af43c1699d050",
+
+       "000000000000000000000000000000000000000000000000",
+       "",
+       "",
+       "000000000000000000000000",
+       "",
+       "cd33b28ac773f74ba00ed1f312572435",
+
+       "000000000000000000000000000000000000000000000000",
+       "00000000000000000000000000000000",
+       "",
+       "000000000000000000000000",
+       "98e7247c07f0fe411c267e4384b0f600",
+       "2ff58d80033927ab8ef4d4587514f0fb",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+       "",
+       "cafebabefacedbaddecaf888",
+       "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256",
+       "9924a7c8587336bfb118024db8674a14",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbaddecaf888",
+       "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710",
+       "2519498e80f1478f37ba55bd6d27618c",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbad",
+       "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+       "65dcc57fcf623a24094fcca40d3533f8",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+       "d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b",
+       "dcf566ff291c25bbb8568fc3d376a6d9",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "",
+       "",
+       "000000000000000000000000",
+       "",
+       "530f8afbc74536b9a963b4f1c4cb738b",
+
+       "0000000000000000000000000000000000000000000000000000000000000000",
+       "00000000000000000000000000000000",
+       "",
+       "000000000000000000000000",
+       "cea7403d4d606b6e074ec5d3baf39d18",
+       "d0d1c8a799996bf0265b98b5d48ab919",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+       "",
+       "cafebabefacedbaddecaf888",
+       "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+       "b094dac5d93471bdec1a502270e3cc6c",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbaddecaf888",
+       "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
+       "76fc6ece0f4e1768cddf8853bb2d551b",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "cafebabefacedbad",
+       "c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f",
+       "3a337dbf46a792c45e454913fe2ea8f2",
+
+       "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+       "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+       "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+       "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+       "5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f",
+       "a44a8266ee1c8eb0c8b5d4cf5ae9f19a",
+
+       NULL
+};
+
+static void
+test_GCM(void)
+{
+       size_t u;
+
+       printf("Test GCM: ");
+       fflush(stdout);
+
+       for (u = 0; KAT_GCM[u]; u += 6) {
+               unsigned char key[32];
+               unsigned char plain[100];
+               unsigned char aad[100];
+               unsigned char iv[100];
+               unsigned char cipher[100];
+               unsigned char tag[100];
+               size_t key_len, plain_len, aad_len, iv_len;
+               br_aes_ct_ctr_keys bc;
+               br_gcm_context gc;
+               unsigned char tmp[100], out[16];
+               size_t v, tag_len;
+
+               key_len = hextobin(key, KAT_GCM[u]);
+               plain_len = hextobin(plain, KAT_GCM[u + 1]);
+               aad_len = hextobin(aad, KAT_GCM[u + 2]);
+               iv_len = hextobin(iv, KAT_GCM[u + 3]);
+               hextobin(cipher, KAT_GCM[u + 4]);
+               hextobin(tag, KAT_GCM[u + 5]);
+
+               br_aes_ct_ctr_init(&bc, key, key_len);
+               br_gcm_init(&gc, &bc.vtable, br_ghash_ctmul32);
+
+               memset(tmp, 0x54, sizeof tmp);
+
+               /*
+                * Basic operation.
+                */
+               memcpy(tmp, plain, plain_len);
+               br_gcm_reset(&gc, iv, iv_len);
+               br_gcm_aad_inject(&gc, aad, aad_len);
+               br_gcm_flip(&gc);
+               br_gcm_run(&gc, 1, tmp, plain_len);
+               br_gcm_get_tag(&gc, out);
+               check_equals("KAT GCM 1", tmp, cipher, plain_len);
+               check_equals("KAT GCM 2", out, tag, 16);
+
+               br_gcm_reset(&gc, iv, iv_len);
+               br_gcm_aad_inject(&gc, aad, aad_len);
+               br_gcm_flip(&gc);
+               br_gcm_run(&gc, 0, tmp, plain_len);
+               check_equals("KAT GCM 3", tmp, plain, plain_len);
+               if (!br_gcm_check_tag(&gc, tag)) {
+                       fprintf(stderr, "Tag not verified (1)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               for (v = plain_len; v < sizeof tmp; v ++) {
+                       if (tmp[v] != 0x54) {
+                               fprintf(stderr, "overflow on data\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /*
+                * Byte-by-byte injection.
+                */
+               br_gcm_reset(&gc, iv, iv_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_gcm_aad_inject(&gc, aad + v, 1);
+               }
+               br_gcm_flip(&gc);
+               for (v = 0; v < plain_len; v ++) {
+                       br_gcm_run(&gc, 1, tmp + v, 1);
+               }
+               check_equals("KAT GCM 4", tmp, cipher, plain_len);
+               if (!br_gcm_check_tag(&gc, tag)) {
+                       fprintf(stderr, "Tag not verified (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               br_gcm_reset(&gc, iv, iv_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_gcm_aad_inject(&gc, aad + v, 1);
+               }
+               br_gcm_flip(&gc);
+               for (v = 0; v < plain_len; v ++) {
+                       br_gcm_run(&gc, 0, tmp + v, 1);
+               }
+               br_gcm_get_tag(&gc, out);
+               check_equals("KAT GCM 5", tmp, plain, plain_len);
+               check_equals("KAT GCM 6", out, tag, 16);
+
+               /*
+                * Check that alterations are detected.
+                */
+               for (v = 0; v < aad_len; v ++) {
+                       memcpy(tmp, cipher, plain_len);
+                       br_gcm_reset(&gc, iv, iv_len);
+                       aad[v] ^= 0x04;
+                       br_gcm_aad_inject(&gc, aad, aad_len);
+                       aad[v] ^= 0x04;
+                       br_gcm_flip(&gc);
+                       br_gcm_run(&gc, 0, tmp, plain_len);
+                       check_equals("KAT GCM 7", tmp, plain, plain_len);
+                       if (br_gcm_check_tag(&gc, tag)) {
+                               fprintf(stderr, "Tag should have changed\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /*
+                * Tag truncation.
+                */
+               for (tag_len = 1; tag_len <= 16; tag_len ++) {
+                       memset(out, 0x54, sizeof out);
+                       memcpy(tmp, plain, plain_len);
+                       br_gcm_reset(&gc, iv, iv_len);
+                       br_gcm_aad_inject(&gc, aad, aad_len);
+                       br_gcm_flip(&gc);
+                       br_gcm_run(&gc, 1, tmp, plain_len);
+                       br_gcm_get_tag_trunc(&gc, out, tag_len);
+                       check_equals("KAT GCM 8", out, tag, tag_len);
+                       for (v = tag_len; v < sizeof out; v ++) {
+                               if (out[v] != 0x54) {
+                                       fprintf(stderr, "overflow on tag\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                       }
+
+                       memcpy(tmp, plain, plain_len);
+                       br_gcm_reset(&gc, iv, iv_len);
+                       br_gcm_aad_inject(&gc, aad, aad_len);
+                       br_gcm_flip(&gc);
+                       br_gcm_run(&gc, 1, tmp, plain_len);
+                       if (!br_gcm_check_tag_trunc(&gc, out, tag_len)) {
+                               fprintf(stderr, "Tag not verified (3)\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+/*
+ * From "The EAX Mode of Operation (A Two-Pass Authenticated Encryption
+ * Scheme Optimized for Simplicity and Efficiency)" (Bellare, Rogaway,
+ * Wagner), presented at FSE 2004. Full article is available at:
+ *   http://web.cs.ucdavis.edu/~rogaway/papers/eax.html
+ *
+ * EAX specification concatenates the authentication tag at the end of
+ * the ciphertext; in our API and the vectors below, the tag is separate.
+ *
+ * Order is: plaintext, key, nonce, header, ciphertext, tag.
+ */
+static const char *const KAT_EAX[] = {
+       "",
+       "233952dee4d5ed5f9b9c6d6ff80ff478",
+       "62ec67f9c3a4a407fcb2a8c49031a8b3",
+       "6bfb914fd07eae6b",
+       "",
+       "e037830e8389f27b025a2d6527e79d01",
+
+       "f7fb",
+       "91945d3f4dcbee0bf45ef52255f095a4",
+       "becaf043b0a23d843194ba972c66debd",
+       "fa3bfd4806eb53fa",
+       "19dd",
+       "5c4c9331049d0bdab0277408f67967e5",
+
+       "1a47cb4933",
+       "01f74ad64077f2e704c0f60ada3dd523",
+       "70c3db4f0d26368400a10ed05d2bff5e",
+       "234a3463c1264ac6",
+       "d851d5bae0",
+       "3a59f238a23e39199dc9266626c40f80",
+
+       "481c9e39b1",
+       "d07cf6cbb7f313bdde66b727afd3c5e8",
+       "8408dfff3c1a2b1292dc199e46b7d617",
+       "33cce2eabff5a79d",
+       "632a9d131a",
+       "d4c168a4225d8e1ff755939974a7bede",
+
+       "40d0c07da5e4",
+       "35b6d0580005bbc12b0587124557d2c2",
+       "fdb6b06676eedc5c61d74276e1f8e816",
+       "aeb96eaebe2970e9",
+       "071dfe16c675",
+       "cb0677e536f73afe6a14b74ee49844dd",
+
+       "4de3b35c3fc039245bd1fb7d",
+       "bd8e6e11475e60b268784c38c62feb22",
+       "6eac5c93072d8e8513f750935e46da1b",
+       "d4482d1ca78dce0f",
+       "835bb4f15d743e350e728414",
+       "abb8644fd6ccb86947c5e10590210a4f",
+
+       "8b0a79306c9ce7ed99dae4f87f8dd61636",
+       "7c77d6e813bed5ac98baa417477a2e7d",
+       "1a8c98dcd73d38393b2bf1569deefc19",
+       "65d2017990d62528",
+       "02083e3979da014812f59f11d52630da30",
+       "137327d10649b0aa6e1c181db617d7f2",
+
+       "1bda122bce8a8dbaf1877d962b8592dd2d56",
+       "5fff20cafab119ca2fc73549e20f5b0d",
+       "dde59b97d722156d4d9aff2bc7559826",
+       "54b9f04e6a09189a",
+       "2ec47b2c4954a489afc7ba4897edcdae8cc3",
+       "3b60450599bd02c96382902aef7f832a",
+
+       "6cf36720872b8513f6eab1a8a44438d5ef11",
+       "a4a4782bcffd3ec5e7ef6d8c34a56123",
+       "b781fcf2f75fa5a8de97a9ca48e522ec",
+       "899a175897561d7e",
+       "0de18fd0fdd91e7af19f1d8ee8733938b1e8",
+       "e7f6d2231618102fdb7fe55ff1991700",
+
+       "ca40d7446e545ffaed3bd12a740a659ffbbb3ceab7",
+       "8395fcf1e95bebd697bd010bc766aac3",
+       "22e7add93cfc6393c57ec0b3c17d6b44",
+       "126735fcc320d25a",
+       "cb8920f87a6c75cff39627b56e3ed197c552d295a7",
+       "cfc46afc253b4652b1af3795b124ab6e",
+
+       NULL
+};
+
+static void
+test_EAX_inner(const char *name, const br_block_ctrcbc_class *vt)
+{
+       size_t u;
+
+       printf("Test EAX %s: ", name);
+       fflush(stdout);
+
+       for (u = 0; KAT_EAX[u]; u += 6) {
+               unsigned char plain[100];
+               unsigned char key[32];
+               unsigned char nonce[100];
+               unsigned char aad[100];
+               unsigned char cipher[100];
+               unsigned char tag[100];
+               size_t plain_len, key_len, nonce_len, aad_len;
+               br_aes_gen_ctrcbc_keys bc;
+               br_eax_context ec;
+               br_eax_state st;
+               unsigned char tmp[100], out[16];
+               size_t v, tag_len;
+
+               plain_len = hextobin(plain, KAT_EAX[u]);
+               key_len = hextobin(key, KAT_EAX[u + 1]);
+               nonce_len = hextobin(nonce, KAT_EAX[u + 2]);
+               aad_len = hextobin(aad, KAT_EAX[u + 3]);
+               hextobin(cipher, KAT_EAX[u + 4]);
+               hextobin(tag, KAT_EAX[u + 5]);
+
+               vt->init(&bc.vtable, key, key_len);
+               br_eax_init(&ec, &bc.vtable);
+
+               memset(tmp, 0x54, sizeof tmp);
+
+               /*
+                * Basic operation.
+                */
+               memcpy(tmp, plain, plain_len);
+               br_eax_reset(&ec, nonce, nonce_len);
+               br_eax_aad_inject(&ec, aad, aad_len);
+               br_eax_flip(&ec);
+               br_eax_run(&ec, 1, tmp, plain_len);
+               br_eax_get_tag(&ec, out);
+               check_equals("KAT EAX 1", tmp, cipher, plain_len);
+               check_equals("KAT EAX 2", out, tag, 16);
+
+               br_eax_reset(&ec, nonce, nonce_len);
+               br_eax_aad_inject(&ec, aad, aad_len);
+               br_eax_flip(&ec);
+               br_eax_run(&ec, 0, tmp, plain_len);
+               check_equals("KAT EAX 3", tmp, plain, plain_len);
+               if (!br_eax_check_tag(&ec, tag)) {
+                       fprintf(stderr, "Tag not verified (1)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               for (v = plain_len; v < sizeof tmp; v ++) {
+                       if (tmp[v] != 0x54) {
+                               fprintf(stderr, "overflow on data\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /*
+                * Byte-by-byte injection.
+                */
+               br_eax_reset(&ec, nonce, nonce_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_eax_aad_inject(&ec, aad + v, 1);
+               }
+               br_eax_flip(&ec);
+               for (v = 0; v < plain_len; v ++) {
+                       br_eax_run(&ec, 1, tmp + v, 1);
+               }
+               check_equals("KAT EAX 4", tmp, cipher, plain_len);
+               if (!br_eax_check_tag(&ec, tag)) {
+                       fprintf(stderr, "Tag not verified (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               br_eax_reset(&ec, nonce, nonce_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_eax_aad_inject(&ec, aad + v, 1);
+               }
+               br_eax_flip(&ec);
+               for (v = 0; v < plain_len; v ++) {
+                       br_eax_run(&ec, 0, tmp + v, 1);
+               }
+               br_eax_get_tag(&ec, out);
+               check_equals("KAT EAX 5", tmp, plain, plain_len);
+               check_equals("KAT EAX 6", out, tag, 16);
+
+               /*
+                * Check that alterations are detected.
+                */
+               for (v = 0; v < aad_len; v ++) {
+                       memcpy(tmp, cipher, plain_len);
+                       br_eax_reset(&ec, nonce, nonce_len);
+                       aad[v] ^= 0x04;
+                       br_eax_aad_inject(&ec, aad, aad_len);
+                       aad[v] ^= 0x04;
+                       br_eax_flip(&ec);
+                       br_eax_run(&ec, 0, tmp, plain_len);
+                       check_equals("KAT EAX 7", tmp, plain, plain_len);
+                       if (br_eax_check_tag(&ec, tag)) {
+                               fprintf(stderr, "Tag should have changed\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /*
+                * Tag truncation.
+                */
+               for (tag_len = 1; tag_len <= 16; tag_len ++) {
+                       memset(out, 0x54, sizeof out);
+                       memcpy(tmp, plain, plain_len);
+                       br_eax_reset(&ec, nonce, nonce_len);
+                       br_eax_aad_inject(&ec, aad, aad_len);
+                       br_eax_flip(&ec);
+                       br_eax_run(&ec, 1, tmp, plain_len);
+                       br_eax_get_tag_trunc(&ec, out, tag_len);
+                       check_equals("KAT EAX 8", out, tag, tag_len);
+                       for (v = tag_len; v < sizeof out; v ++) {
+                               if (out[v] != 0x54) {
+                                       fprintf(stderr, "overflow on tag\n");
+                                       exit(EXIT_FAILURE);
+                               }
+                       }
+
+                       memcpy(tmp, plain, plain_len);
+                       br_eax_reset(&ec, nonce, nonce_len);
+                       br_eax_aad_inject(&ec, aad, aad_len);
+                       br_eax_flip(&ec);
+                       br_eax_run(&ec, 1, tmp, plain_len);
+                       if (!br_eax_check_tag_trunc(&ec, out, tag_len)) {
+                               fprintf(stderr, "Tag not verified (3)\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               printf(".");
+               fflush(stdout);
+
+               /*
+                * For capture tests, we need the message to be non-empty.
+                */
+               if (plain_len == 0) {
+                       continue;
+               }
+
+               /*
+                * Captured state, pre-AAD. This requires the AAD and the
+                * message to be non-empty.
+                */
+               br_eax_capture(&ec, &st);
+
+               if (aad_len > 0) {
+                       br_eax_reset_pre_aad(&ec, &st, nonce, nonce_len);
+                       br_eax_aad_inject(&ec, aad, aad_len);
+                       br_eax_flip(&ec);
+                       memcpy(tmp, plain, plain_len);
+                       br_eax_run(&ec, 1, tmp, plain_len);
+                       br_eax_get_tag(&ec, out);
+                       check_equals("KAT EAX 9", tmp, cipher, plain_len);
+                       check_equals("KAT EAX 10", out, tag, 16);
+
+                       br_eax_reset_pre_aad(&ec, &st, nonce, nonce_len);
+                       br_eax_aad_inject(&ec, aad, aad_len);
+                       br_eax_flip(&ec);
+                       br_eax_run(&ec, 0, tmp, plain_len);
+                       br_eax_get_tag(&ec, out);
+                       check_equals("KAT EAX 11", tmp, plain, plain_len);
+                       check_equals("KAT EAX 12", out, tag, 16);
+               }
+
+               /*
+                * Captured state, post-AAD. This requires the message to
+                * be non-empty.
+                */
+               br_eax_reset(&ec, nonce, nonce_len);
+               br_eax_aad_inject(&ec, aad, aad_len);
+               br_eax_flip(&ec);
+               br_eax_get_aad_mac(&ec, &st);
+
+               br_eax_reset_post_aad(&ec, &st, nonce, nonce_len);
+               memcpy(tmp, plain, plain_len);
+               br_eax_run(&ec, 1, tmp, plain_len);
+               br_eax_get_tag(&ec, out);
+               check_equals("KAT EAX 13", tmp, cipher, plain_len);
+               check_equals("KAT EAX 14", out, tag, 16);
+
+               br_eax_reset_post_aad(&ec, &st, nonce, nonce_len);
+               br_eax_run(&ec, 0, tmp, plain_len);
+               br_eax_get_tag(&ec, out);
+               check_equals("KAT EAX 15", tmp, plain, plain_len);
+               check_equals("KAT EAX 16", out, tag, 16);
+
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+static void
+test_EAX(void)
+{
+       const br_block_ctrcbc_class *x_ctrcbc;
+
+       test_EAX_inner("aes_big", &br_aes_big_ctrcbc_vtable);
+       test_EAX_inner("aes_small", &br_aes_small_ctrcbc_vtable);
+       test_EAX_inner("aes_ct", &br_aes_ct_ctrcbc_vtable);
+       test_EAX_inner("aes_ct64", &br_aes_ct64_ctrcbc_vtable);
+
+       x_ctrcbc = br_aes_x86ni_ctrcbc_get_vtable();
+       if (x_ctrcbc != NULL) {
+               test_EAX_inner("aes_x86ni", x_ctrcbc);
+       } else {
+               printf("Test EAX aes_x86ni: UNAVAILABLE\n");
+       }
+}
+
+/*
+ * From NIST SP 800-38C, appendix C.
+ *
+ * CCM specification concatenates the authentication tag at the end of
+ * the ciphertext; in our API and the vectors below, the tag is separate.
+ *
+ * Order is: key, nonce, aad, plaintext, ciphertext, tag.
+ */
+static const char *const KAT_CCM[] = {
+       "404142434445464748494a4b4c4d4e4f",
+       "10111213141516",
+       "0001020304050607",
+       "20212223",
+       "7162015b",
+       "4dac255d",
+
+       "404142434445464748494a4b4c4d4e4f",
+       "1011121314151617",
+       "000102030405060708090a0b0c0d0e0f",
+       "202122232425262728292a2b2c2d2e2f",
+       "d2a1f0e051ea5f62081a7792073d593d",
+       "1fc64fbfaccd",
+
+       "404142434445464748494a4b4c4d4e4f",
+       "101112131415161718191a1b",
+       "000102030405060708090a0b0c0d0e0f10111213",
+       "202122232425262728292a2b2c2d2e2f3031323334353637",
+       "e3b201a9f5b71a7a9b1ceaeccd97e70b6176aad9a4428aa5",
+       "484392fbc1b09951",
+
+       "404142434445464748494a4b4c4d4e4f",
+       "101112131415161718191a1b1c",
+       NULL,
+       "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f",
+       "69915dad1e84c6376a68c2967e4dab615ae0fd1faec44cc484828529463ccf72",
+       "b4ac6bec93e8598e7f0dadbcea5b",
+
+       NULL
+};
+
+static void
+test_CCM_inner(const char *name, const br_block_ctrcbc_class *vt)
+{
+       size_t u;
+
+       printf("Test CCM %s: ", name);
+       fflush(stdout);
+
+       for (u = 0; KAT_CCM[u]; u += 6) {
+               unsigned char plain[100];
+               unsigned char key[32];
+               unsigned char nonce[100];
+               unsigned char aad_buf[100], *aad;
+               unsigned char cipher[100];
+               unsigned char tag[100];
+               size_t plain_len, key_len, nonce_len, aad_len, tag_len;
+               br_aes_gen_ctrcbc_keys bc;
+               br_ccm_context ec;
+               unsigned char tmp[100], out[16];
+               size_t v;
+
+               key_len = hextobin(key, KAT_CCM[u]);
+               nonce_len = hextobin(nonce, KAT_CCM[u + 1]);
+               if (KAT_CCM[u + 2] == NULL) {
+                       aad_len = 65536;
+                       aad = malloc(aad_len);
+                       if (aad == NULL) {
+                               fprintf(stderr, "OOM error\n");
+                               exit(EXIT_FAILURE);
+                       }
+                       for (v = 0; v < 65536; v ++) {
+                               aad[v] = (unsigned char)v;
+                       }
+               } else {
+                       aad = aad_buf;
+                       aad_len = hextobin(aad, KAT_CCM[u + 2]);
+               }
+               plain_len = hextobin(plain, KAT_CCM[u + 3]);
+               hextobin(cipher, KAT_CCM[u + 4]);
+               tag_len = hextobin(tag, KAT_CCM[u + 5]);
+
+               vt->init(&bc.vtable, key, key_len);
+               br_ccm_init(&ec, &bc.vtable);
+
+               memset(tmp, 0x54, sizeof tmp);
+
+               /*
+                * Basic operation.
+                */
+               memcpy(tmp, plain, plain_len);
+               if (!br_ccm_reset(&ec, nonce, nonce_len,
+                       aad_len, plain_len, tag_len))
+               {
+                       fprintf(stderr, "CCM reset failed\n");
+                       exit(EXIT_FAILURE);
+               }
+               br_ccm_aad_inject(&ec, aad, aad_len);
+               br_ccm_flip(&ec);
+               br_ccm_run(&ec, 1, tmp, plain_len);
+               if (br_ccm_get_tag(&ec, out) != tag_len) {
+                       fprintf(stderr, "CCM returned wrong tag length\n");
+                       exit(EXIT_FAILURE);
+               }
+               check_equals("KAT CCM 1", tmp, cipher, plain_len);
+               check_equals("KAT CCM 2", out, tag, tag_len);
+
+               br_ccm_reset(&ec, nonce, nonce_len,
+                       aad_len, plain_len, tag_len);
+               br_ccm_aad_inject(&ec, aad, aad_len);
+               br_ccm_flip(&ec);
+               br_ccm_run(&ec, 0, tmp, plain_len);
+               check_equals("KAT CCM 3", tmp, plain, plain_len);
+               if (!br_ccm_check_tag(&ec, tag)) {
+                       fprintf(stderr, "Tag not verified (1)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               for (v = plain_len; v < sizeof tmp; v ++) {
+                       if (tmp[v] != 0x54) {
+                               fprintf(stderr, "overflow on data\n");
+                               exit(EXIT_FAILURE);
+                       }
+               }
+
+               /*
+                * Byte-by-byte injection.
+                */
+               br_ccm_reset(&ec, nonce, nonce_len,
+                       aad_len, plain_len, tag_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_ccm_aad_inject(&ec, aad + v, 1);
+               }
+               br_ccm_flip(&ec);
+               for (v = 0; v < plain_len; v ++) {
+                       br_ccm_run(&ec, 1, tmp + v, 1);
+               }
+               check_equals("KAT CCM 4", tmp, cipher, plain_len);
+               if (!br_ccm_check_tag(&ec, tag)) {
+                       fprintf(stderr, "Tag not verified (2)\n");
+                       exit(EXIT_FAILURE);
+               }
+
+               br_ccm_reset(&ec, nonce, nonce_len,
+                       aad_len, plain_len, tag_len);
+               for (v = 0; v < aad_len; v ++) {
+                       br_ccm_aad_inject(&ec, aad + v, 1);
+               }
+               br_ccm_flip(&ec);
+               for (v = 0; v < plain_len; v ++) {
+                       br_ccm_run(&ec, 0, tmp + v, 1);
+               }
+               br_ccm_get_tag(&ec, out);
+               check_equals("KAT CCM 5", tmp, plain, plain_len);
+               check_equals("KAT CCM 6", out, tag, tag_len);
+
+               /*
+                * Check that alterations are detected.
+                */
+               for (v = 0; v < aad_len; v ++) {
+                       memcpy(tmp, cipher, plain_len);
+                       br_ccm_reset(&ec, nonce, nonce_len,
+                               aad_len, plain_len, tag_len);
+                       aad[v] ^= 0x04;
+                       br_ccm_aad_inject(&ec, aad, aad_len);
+                       aad[v] ^= 0x04;
+                       br_ccm_flip(&ec);
+                       br_ccm_run(&ec, 0, tmp, plain_len);
+                       check_equals("KAT CCM 7", tmp, plain, plain_len);
+                       if (br_ccm_check_tag(&ec, tag)) {
+                               fprintf(stderr, "Tag should have changed\n");
+                               exit(EXIT_FAILURE);
+                       }
+
+                       /*
+                        * When the AAD is really big, we don't want to do
+                        * the complete quadratic operation.
+                        */
+                       if (v >= 32) {
+                               break;
+                       }
+               }
+
+               if (aad != aad_buf) {
+                       free(aad);
+               }
+
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+static void
+test_CCM(void)
+{
+       const br_block_ctrcbc_class *x_ctrcbc;
+
+       test_CCM_inner("aes_big", &br_aes_big_ctrcbc_vtable);
+       test_CCM_inner("aes_small", &br_aes_small_ctrcbc_vtable);
+       test_CCM_inner("aes_ct", &br_aes_ct_ctrcbc_vtable);
+       test_CCM_inner("aes_ct64", &br_aes_ct64_ctrcbc_vtable);
+
+       x_ctrcbc = br_aes_x86ni_ctrcbc_get_vtable();
+       if (x_ctrcbc != NULL) {
+               test_CCM_inner("aes_x86ni", x_ctrcbc);
+       } else {
+               printf("Test CCM aes_x86ni: UNAVAILABLE\n");
+       }
+}
+
+static void
+test_EC_inner(const char *sk, const char *sU,
+       const br_ec_impl *impl, int curve)
+{
+       unsigned char bk[70];
+       unsigned char eG[150], eU[150];
+       uint32_t n[22], n0i;
+       size_t klen, ulen, nlen;
+       const br_ec_curve_def *cd;
+       br_hmac_drbg_context rng;
+       int i;
+
+       klen = hextobin(bk, sk);
+       ulen = hextobin(eU, sU);
+       switch (curve) {
+       case BR_EC_secp256r1:
+               cd = &br_secp256r1;
+               break;
+       case BR_EC_secp384r1:
+               cd = &br_secp384r1;
+               break;
+       case BR_EC_secp521r1:
+               cd = &br_secp521r1;
+               break;
+       default:
+               fprintf(stderr, "Unknown curve: %d\n", curve);
+               exit(EXIT_FAILURE);
+               break;
+       }
+       if (ulen != cd->generator_len) {
+               fprintf(stderr, "KAT vector wrong (%lu / %lu)\n",
+                       (unsigned long)ulen,
+                       (unsigned long)cd->generator_len);
+       }
+       memcpy(eG, cd->generator, ulen);
+       if (impl->mul(eG, ulen, bk, klen, curve) != 1) {
+               fprintf(stderr, "KAT multiplication failed\n");
+               exit(EXIT_FAILURE);
+       }
+       if (memcmp(eG, eU, ulen) != 0) {
+               fprintf(stderr, "KAT mul: mismatch\n");
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        * Test the two-point-mul function. We want to test the basic
+        * functionality, and the following special cases:
+        *   x = y
+        *   x + y = curve order
+        */
+       nlen = cd->order_len;
+       br_i31_decode(n, cd->order, nlen);
+       n0i = br_i31_ninv31(n[1]);
+       br_hmac_drbg_init(&rng, &br_sha256_vtable, "seed for EC", 11);
+       for (i = 0; i < 10; i ++) {
+               unsigned char ba[80], bb[80], bx[80], by[80], bz[80];
+               uint32_t a[22], b[22], x[22], y[22], z[22], t1[22], t2[22];
+               uint32_t r;
+               unsigned char eA[160], eB[160], eC[160], eD[160];
+
+               /*
+                * Generate random a and b, and compute A = a*G and B = b*G.
+                */
+               br_hmac_drbg_generate(&rng, ba, sizeof ba);
+               br_i31_decode_reduce(a, ba, sizeof ba, n);
+               br_i31_encode(ba, nlen, a);
+               br_hmac_drbg_generate(&rng, bb, sizeof bb);
+               br_i31_decode_reduce(b, bb, sizeof bb, n);
+               br_i31_encode(bb, nlen, b);
+               memcpy(eA, cd->generator, ulen);
+               impl->mul(eA, ulen, ba, nlen, cd->curve);
+               memcpy(eB, cd->generator, ulen);
+               impl->mul(eB, ulen, bb, nlen, cd->curve);
+
+               /*
+                * Generate random x and y (modulo n).
+                */
+               br_hmac_drbg_generate(&rng, bx, sizeof bx);
+               br_i31_decode_reduce(x, bx, sizeof bx, n);
+               br_i31_encode(bx, nlen, x);
+               br_hmac_drbg_generate(&rng, by, sizeof by);
+               br_i31_decode_reduce(y, by, sizeof by, n);
+               br_i31_encode(by, nlen, y);
+
+               /*
+                * Compute z = a*x + b*y (mod n).
+                */
+               memcpy(t1, x, sizeof x);
+               br_i31_to_monty(t1, n);
+               br_i31_montymul(z, a, t1, n, n0i);
+               memcpy(t1, y, sizeof y);
+               br_i31_to_monty(t1, n);
+               br_i31_montymul(t2, b, t1, n, n0i);
+               r = br_i31_add(z, t2, 1);
+               r |= br_i31_sub(z, n, 0) ^ 1;
+               br_i31_sub(z, n, r);
+               br_i31_encode(bz, nlen, z);
+
+               /*
+                * Compute C = x*A + y*B with muladd(), and also
+                * D = z*G with mul(). The two points must match.
                 */
                memcpy(eC, eA, ulen);
                if (impl->muladd(eC, eB, ulen,
                 */
                memcpy(eC, eA, ulen);
                if (impl->muladd(eC, eB, ulen,
@@ -4863,6 +6135,39 @@ test_EC_inner(const char *sk, const char *sU,
        fflush(stdout);
 }
 
        fflush(stdout);
 }
 
+static void
+test_EC_P256_carry_inner(const br_ec_impl *impl, const char *sP, const char *sQ)
+{
+       unsigned char P[65], Q[sizeof P], k[1];
+       size_t plen, qlen;
+
+       plen = hextobin(P, sP);
+       qlen = hextobin(Q, sQ);
+       if (plen != sizeof P || qlen != sizeof P) {
+               fprintf(stderr, "KAT is incorrect\n");
+               exit(EXIT_FAILURE);
+       }
+       k[0] = 0x10;
+       if (impl->mul(P, plen, k, 1, BR_EC_secp256r1) != 1) {
+               fprintf(stderr, "P-256 multiplication failed\n");
+               exit(EXIT_FAILURE);
+       }
+       check_equals("P256_carry", P, Q, plen);
+       printf(".");
+       fflush(stdout);
+}
+
+static void
+test_EC_P256_carry(const br_ec_impl *impl)
+{
+       test_EC_P256_carry_inner(impl,
+               "0435BAA24B2B6E1B3C88E22A383BD88CC4B9A3166E7BCF94FF6591663AE066B33B821EBA1B4FC8EA609A87EB9A9C9A1CCD5C9F42FA1365306F64D7CAA718B8C978",
+               "0447752A76CA890328D34E675C4971EC629132D1FC4863EDB61219B72C4E58DC5E9D51E7B293488CFD913C3CF20E438BB65C2BA66A7D09EABB45B55E804260C5EB");
+       test_EC_P256_carry_inner(impl,
+               "04DCAE9D9CE211223602024A6933BD42F77B6BF4EAB9C8915F058C149419FADD2CC9FC0707B270A1B5362BA4D249AFC8AC3DA1EFCA8270176EEACA525B49EE19E6",
+               "048DAC7B0BE9B3206FCE8B24B6B4AEB122F2A67D13E536B390B6585CA193427E63F222388B5F51D744D6F5D47536D89EEEC89552BCB269E7828019C4410DFE980A");
+}
+
 static void
 test_EC_KAT(const char *name, const br_ec_impl *impl, uint32_t curve_mask)
 {
 static void
 test_EC_KAT(const char *name, const br_ec_impl *impl, uint32_t curve_mask)
 {
@@ -4875,6 +6180,7 @@ test_EC_KAT(const char *name, const br_ec_impl *impl, uint32_t curve_mask)
                        "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721",
                        "0460FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB67903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299",
                        impl, BR_EC_secp256r1);
                        "C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721",
                        "0460FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB67903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299",
                        impl, BR_EC_secp256r1);
+               test_EC_P256_carry(impl);
        }
        if (curve_mask & ((uint32_t)1 << BR_EC_secp384r1)) {
                test_EC_inner(
        }
        if (curve_mask & ((uint32_t)1 << BR_EC_secp384r1)) {
                test_EC_inner(
@@ -4918,6 +6224,13 @@ test_EC_p256_m15(void)
                (uint32_t)1 << BR_EC_secp256r1);
 }
 
                (uint32_t)1 << BR_EC_secp256r1);
 }
 
+static void
+test_EC_p256_m31(void)
+{
+       test_EC_KAT("EC_p256_m31", &br_ec_p256_m31,
+               (uint32_t)1 << BR_EC_secp256r1);
+}
+
 const struct {
        const char *scalar;
        const char *u_in;
 const struct {
        const char *scalar;
        const char *u_in;
@@ -4935,13 +6248,13 @@ const struct {
 static void
 test_EC_c25519(const char *name, const br_ec_impl *iec)
 {
 static void
 test_EC_c25519(const char *name, const br_ec_impl *iec)
 {
+       unsigned char bu[32], bk[32], br[32];
        size_t v;
        size_t v;
+       int i;
 
        printf("Test %s: ", name);
        fflush(stdout);
        for (v = 0; C25519_KAT[v].scalar; v ++) {
 
        printf("Test %s: ", name);
        fflush(stdout);
        for (v = 0; C25519_KAT[v].scalar; v ++) {
-               unsigned char bu[32], bk[32], br[32];
-
                hextobin(bk, C25519_KAT[v].scalar);
                hextobin(bu, C25519_KAT[v].u_in);
                hextobin(br, C25519_KAT[v].u_out);
                hextobin(bk, C25519_KAT[v].scalar);
                hextobin(bu, C25519_KAT[v].u_in);
                hextobin(br, C25519_KAT[v].u_out);
@@ -4956,6 +6269,44 @@ test_EC_c25519(const char *name, const br_ec_impl *iec)
                printf(".");
                fflush(stdout);
        }
                printf(".");
                fflush(stdout);
        }
+       printf(" ");
+       fflush(stdout);
+
+       memset(bu, 0, sizeof bu);
+       bu[0] = 0x09;
+       memcpy(bk, bu, sizeof bu);
+       for (i = 1; i <= 1000; i ++) {
+               if (!iec->mul(bu, sizeof bu, bk, sizeof bk, BR_EC_curve25519)) {
+                       fprintf(stderr, "Curve25519 multiplication failed"
+                               " (iter=%d)\n", i);
+                       exit(EXIT_FAILURE);
+               }
+               for (v = 0; v < sizeof bu; v ++) {
+                       unsigned t;
+
+                       t = bu[v];
+                       bu[v] = bk[v];
+                       bk[v] = t;
+               }
+               if (i == 1 || i == 1000) {
+                       const char *sref;
+
+                       sref = (i == 1)
+                               ? "422C8E7A6227D7BCA1350B3E2BB7279F7897B87BB6854B783C60E80311AE3079"
+                               : "684CF59BA83309552800EF566F2F4D3C1C3887C49360E3875F2EB94D99532C51";
+                       hextobin(br, sref);
+                       if (memcmp(bk, br, sizeof bk) != 0) {
+                               fprintf(stderr,
+                                       "Curve25519 failed KAT (iter=%d)\n", i);
+                               exit(EXIT_FAILURE);
+                       }
+               }
+               if (i % 100 == 0) {
+                       printf(".");
+                       fflush(stdout);
+               }
+       }
+
        printf(" done.\n");
        fflush(stdout);
 }
        printf(" done.\n");
        fflush(stdout);
 }
@@ -4966,6 +6317,24 @@ test_EC_c25519_i15(void)
        test_EC_c25519("EC_c25519_i15", &br_ec_c25519_i15);
 }
 
        test_EC_c25519("EC_c25519_i15", &br_ec_c25519_i15);
 }
 
+static void
+test_EC_c25519_i31(void)
+{
+       test_EC_c25519("EC_c25519_i31", &br_ec_c25519_i31);
+}
+
+static void
+test_EC_c25519_m15(void)
+{
+       test_EC_c25519("EC_c25519_m15", &br_ec_c25519_m15);
+}
+
+static void
+test_EC_c25519_m31(void)
+{
+       test_EC_c25519("EC_c25519_m31", &br_ec_c25519_m31);
+}
+
 static const unsigned char EC_P256_PUB_POINT[] = {
        0x04, 0x60, 0xFE, 0xD4, 0xBA, 0x25, 0x5A, 0x9D,
        0x31, 0xC9, 0x61, 0xEB, 0x74, 0xC6, 0x35, 0x6D,
 static const unsigned char EC_P256_PUB_POINT[] = {
        0x04, 0x60, 0xFE, 0xD4, 0xBA, 0x25, 0x5A, 0x9D,
        0x31, 0xC9, 0x61, 0xEB, 0x74, 0xC6, 0x35, 0x6D,
@@ -5436,6 +6805,108 @@ test_ECDSA_i15(void)
        fflush(stdout);
 }
 
        fflush(stdout);
 }
 
+static void
+test_modpow_i31(void)
+{
+       br_hmac_drbg_context hc;
+       int k;
+
+       printf("Test ModPow/i31: ");
+
+       br_hmac_drbg_init(&hc, &br_sha256_vtable, "seed modpow", 11);
+       for (k = 10; k <= 500; k ++) {
+               size_t blen;
+               unsigned char bm[128], bx[128], bx1[128], bx2[128];
+               unsigned char be[128];
+               unsigned mask;
+               uint32_t x1[35], m1[35];
+               uint16_t x2[70], m2[70];
+               uint32_t tmp1[1000];
+               uint16_t tmp2[2000];
+
+               blen = (k + 7) >> 3;
+               br_hmac_drbg_generate(&hc, bm, blen);
+               br_hmac_drbg_generate(&hc, bx, blen);
+               br_hmac_drbg_generate(&hc, be, blen);
+               bm[blen - 1] |= 0x01;
+               mask = 0xFF >> ((int)(blen << 3) - k);
+               bm[0] &= mask;
+               bm[0] |= (mask - (mask >> 1));
+               bx[0] &= (mask >> 1);
+
+               br_i31_decode(m1, bm, blen);
+               br_i31_decode_mod(x1, bx, blen, m1);
+               br_i31_modpow_opt(x1, be, blen, m1, br_i31_ninv31(m1[1]),
+                       tmp1, (sizeof tmp1) / (sizeof tmp1[0]));
+               br_i31_encode(bx1, blen, x1);
+
+               br_i15_decode(m2, bm, blen);
+               br_i15_decode_mod(x2, bx, blen, m2);
+               br_i15_modpow_opt(x2, be, blen, m2, br_i15_ninv15(m2[1]),
+                       tmp2, (sizeof tmp2) / (sizeof tmp2[0]));
+               br_i15_encode(bx2, blen, x2);
+
+               check_equals("ModPow i31/i15", bx1, bx2, blen);
+
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
+static void
+test_modpow_i62(void)
+{
+       br_hmac_drbg_context hc;
+       int k;
+
+       printf("Test ModPow/i62: ");
+
+       br_hmac_drbg_init(&hc, &br_sha256_vtable, "seed modpow", 11);
+       for (k = 10; k <= 500; k ++) {
+               size_t blen;
+               unsigned char bm[128], bx[128], bx1[128], bx2[128];
+               unsigned char be[128];
+               unsigned mask;
+               uint32_t x1[35], m1[35];
+               uint16_t x2[70], m2[70];
+               uint64_t tmp1[500];
+               uint16_t tmp2[2000];
+
+               blen = (k + 7) >> 3;
+               br_hmac_drbg_generate(&hc, bm, blen);
+               br_hmac_drbg_generate(&hc, bx, blen);
+               br_hmac_drbg_generate(&hc, be, blen);
+               bm[blen - 1] |= 0x01;
+               mask = 0xFF >> ((int)(blen << 3) - k);
+               bm[0] &= mask;
+               bm[0] |= (mask - (mask >> 1));
+               bx[0] &= (mask >> 1);
+
+               br_i31_decode(m1, bm, blen);
+               br_i31_decode_mod(x1, bx, blen, m1);
+               br_i62_modpow_opt(x1, be, blen, m1, br_i31_ninv31(m1[1]),
+                       tmp1, (sizeof tmp1) / (sizeof tmp1[0]));
+               br_i31_encode(bx1, blen, x1);
+
+               br_i15_decode(m2, bm, blen);
+               br_i15_decode_mod(x2, bx, blen, m2);
+               br_i15_modpow_opt(x2, be, blen, m2, br_i15_ninv15(m2[1]),
+                       tmp2, (sizeof tmp2) / (sizeof tmp2[0]));
+               br_i15_encode(bx2, blen, x2);
+
+               check_equals("ModPow i62/i15", bx1, bx2, blen);
+
+               printf(".");
+               fflush(stdout);
+       }
+
+       printf(" done.\n");
+       fflush(stdout);
+}
+
 static int
 eq_name(const char *s1, const char *s2)
 {
 static int
 eq_name(const char *s1, const char *s2)
 {
@@ -5496,25 +6967,45 @@ static const struct {
        STU(AES_small),
        STU(AES_ct),
        STU(AES_ct64),
        STU(AES_small),
        STU(AES_ct),
        STU(AES_ct64),
+       STU(AES_pwr8),
+       STU(AES_x86ni),
+       STU(AES_CTRCBC_big),
+       STU(AES_CTRCBC_small),
+       STU(AES_CTRCBC_ct),
+       STU(AES_CTRCBC_ct64),
+       STU(AES_CTRCBC_x86ni),
        STU(DES_tab),
        STU(DES_ct),
        STU(ChaCha20_ct),
        STU(DES_tab),
        STU(DES_ct),
        STU(ChaCha20_ct),
+       STU(ChaCha20_sse2),
        STU(Poly1305_ctmul),
        STU(Poly1305_ctmul32),
        STU(Poly1305_ctmul),
        STU(Poly1305_ctmul32),
+       STU(Poly1305_ctmulq),
        STU(Poly1305_i15),
        STU(RSA_i15),
        STU(RSA_i31),
        STU(RSA_i32),
        STU(Poly1305_i15),
        STU(RSA_i15),
        STU(RSA_i31),
        STU(RSA_i32),
+       STU(RSA_i62),
        STU(GHASH_ctmul),
        STU(GHASH_ctmul32),
        STU(GHASH_ctmul64),
        STU(GHASH_ctmul),
        STU(GHASH_ctmul32),
        STU(GHASH_ctmul64),
+       STU(GHASH_pclmul),
+       STU(GHASH_pwr8),
+       STU(CCM),
+       STU(EAX),
+       STU(GCM),
        STU(EC_prime_i15),
        STU(EC_prime_i31),
        STU(EC_p256_m15),
        STU(EC_prime_i15),
        STU(EC_prime_i31),
        STU(EC_p256_m15),
-       /* STU(EC_prime_i32), */
+       STU(EC_p256_m31),
        STU(EC_c25519_i15),
        STU(EC_c25519_i15),
+       STU(EC_c25519_i31),
+       STU(EC_c25519_m15),
+       STU(EC_c25519_m31),
        STU(ECDSA_i15),
        STU(ECDSA_i31),
        STU(ECDSA_i15),
        STU(ECDSA_i31),
+       STU(modpow_i31),
+       STU(modpow_i62),
        { 0, 0 }
 };
 
        { 0, 0 }
 };