Revert "external/boringssl: Sync to 66e61c577d39e757bf491468f651..."

Revert submission 1835013-bssl_update_sep2021

Reason for revert: DroidMonitor: Potential culprit for Bug 201683809 - verifying through Forrest before revert submission. This is part of the standard investigation process, and does not mean your CL will be reverted.
Reverted Changes:
I27d7b79e3:Fix wpa_supplicant build with newer BoringSSL
I4f2228ef8:external/boringssl: Sync to 66e61c577d39e757bf4914...

Change-Id: If2184c4aa55b7dc89e037362e4d5cbbea1107ae2
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index 8f8bdb5..a2129df 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-66e61c577d39e757bf491468f651461fa79fd5e1
+c1571feb5faf5cce844354c63d0f3e842464bea3
diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl
index 91fa515..cd86ac7 100644
--- a/BUILD.generated.bzl
+++ b/BUILD.generated.bzl
@@ -207,7 +207,6 @@
 ]
 
 crypto_internal_headers = [
-    "src/crypto/asn1/charmap.h",
     "src/crypto/asn1/internal.h",
     "src/crypto/bio/internal.h",
     "src/crypto/bytestring/internal.h",
@@ -252,6 +251,7 @@
     "src/crypto/poly1305/internal.h",
     "src/crypto/pool/internal.h",
     "src/crypto/trust_token/internal.h",
+    "src/crypto/x509/charmap.h",
     "src/crypto/x509/internal.h",
     "src/crypto/x509v3/ext_dat.h",
     "src/crypto/x509v3/internal.h",
@@ -276,7 +276,6 @@
     "src/crypto/asn1/a_object.c",
     "src/crypto/asn1/a_octet.c",
     "src/crypto/asn1/a_print.c",
-    "src/crypto/asn1/a_strex.c",
     "src/crypto/asn1/a_strnid.c",
     "src/crypto/asn1/a_time.c",
     "src/crypto/asn1/a_type.c",
@@ -417,13 +416,13 @@
     "src/crypto/trust_token/voprf.c",
     "src/crypto/x509/a_digest.c",
     "src/crypto/x509/a_sign.c",
+    "src/crypto/x509/a_strex.c",
     "src/crypto/x509/a_verify.c",
     "src/crypto/x509/algorithm.c",
     "src/crypto/x509/asn1_gen.c",
     "src/crypto/x509/by_dir.c",
     "src/crypto/x509/by_file.c",
     "src/crypto/x509/i2d_pr.c",
-    "src/crypto/x509/name_print.c",
     "src/crypto/x509/rsa_pss.c",
     "src/crypto/x509/t_crl.c",
     "src/crypto/x509/t_req.c",
diff --git a/BUILD.generated_tests.bzl b/BUILD.generated_tests.bzl
index 905ff9d..ef6c365 100644
--- a/BUILD.generated_tests.bzl
+++ b/BUILD.generated_tests.bzl
@@ -1,7 +1,6 @@
 # This file is created by generate_build_files.py. Do not edit manually.
 
 test_support_sources = [
-    "src/crypto/asn1/charmap.h",
     "src/crypto/asn1/internal.h",
     "src/crypto/bio/internal.h",
     "src/crypto/bytestring/internal.h",
@@ -54,6 +53,7 @@
     "src/crypto/test/wycheproof_util.cc",
     "src/crypto/test/wycheproof_util.h",
     "src/crypto/trust_token/internal.h",
+    "src/crypto/x509/charmap.h",
     "src/crypto/x509/internal.h",
     "src/crypto/x509v3/ext_dat.h",
     "src/crypto/x509v3/internal.h",
diff --git a/UPDATING b/UPDATING
index 0266bfa..2d039fb 100755
--- a/UPDATING
+++ b/UPDATING
@@ -35,5 +35,5 @@
 cp src/LICENSE NOTICE
 
 git add .
-git commit --no-verify -F $msgfile
+git commit -F $msgfile
 rm -f $msgfile
diff --git a/android-sources.cmake b/android-sources.cmake
index 87f833d..1996c28 100644
--- a/android-sources.cmake
+++ b/android-sources.cmake
@@ -30,7 +30,6 @@
   ${BORINGSSL_ROOT}src/crypto/asn1/a_object.c
   ${BORINGSSL_ROOT}src/crypto/asn1/a_octet.c
   ${BORINGSSL_ROOT}src/crypto/asn1/a_print.c
-  ${BORINGSSL_ROOT}src/crypto/asn1/a_strex.c
   ${BORINGSSL_ROOT}src/crypto/asn1/a_strnid.c
   ${BORINGSSL_ROOT}src/crypto/asn1/a_time.c
   ${BORINGSSL_ROOT}src/crypto/asn1/a_type.c
@@ -171,13 +170,13 @@
   ${BORINGSSL_ROOT}src/crypto/trust_token/voprf.c
   ${BORINGSSL_ROOT}src/crypto/x509/a_digest.c
   ${BORINGSSL_ROOT}src/crypto/x509/a_sign.c
+  ${BORINGSSL_ROOT}src/crypto/x509/a_strex.c
   ${BORINGSSL_ROOT}src/crypto/x509/a_verify.c
   ${BORINGSSL_ROOT}src/crypto/x509/algorithm.c
   ${BORINGSSL_ROOT}src/crypto/x509/asn1_gen.c
   ${BORINGSSL_ROOT}src/crypto/x509/by_dir.c
   ${BORINGSSL_ROOT}src/crypto/x509/by_file.c
   ${BORINGSSL_ROOT}src/crypto/x509/i2d_pr.c
-  ${BORINGSSL_ROOT}src/crypto/x509/name_print.c
   ${BORINGSSL_ROOT}src/crypto/x509/rsa_pss.c
   ${BORINGSSL_ROOT}src/crypto/x509/t_crl.c
   ${BORINGSSL_ROOT}src/crypto/x509/t_req.c
diff --git a/crypto_test_data.cc b/crypto_test_data.cc
index 125dc91..450b3cb 100644
--- a/crypto_test_data.cc
+++ b/crypto_test_data.cc
@@ -591,21 +591,21 @@
     "c561\nCT: fe1d463b1466e8e411f0b0700f90760472ee5141f3e5afef43fd729f1623dca75cd4d00576765b335f8b2b77b00527599cb3\nTAG: 111d8540fd5ec04b9ba16ed810133026\n\nKEY: 38e63e8b6402ac3f6d1641a1e3b74d2074be0fe41129975a3ff62b74ca52af05\nNONCE: 228d671b036710cbdaa72e9bf1d9ed6982b0bb3428a69fd6\nIN: 20a8d18878924d09aac32853c10e73dbd741134b7050ae6999839f2dbc727cb0052b5497c4bbd2a89e716278f15c81b871953614a49693\nAD: e9e6ac73\nCT: 80e0fe8eb26e5df229c6d939c944d440a37aa3cabf76eab5b9a420095513021ea4241ab367f6f44a20817b14631549ae6c96aa963970e1\nTAG: 1e80fbafcc7168e0494fce4cd76d692c\n\nKEY: 4325dd8406fdb8431a81f1b5db3603995256de36121019724cca2190c87a6e83\nNONCE: dcbf3077b36d5d678d668fd2d0c99284c780b55c4658ea75\nIN: 4f599ad04f79be9add10fdc649b8be53e1062ea5e9c2bed22265dc6fb30d5ab4fd4425b38ff14d8e68013405bec1eff8c9ef3069902e492aac73dcd9\nAD: 6fa0d757\nCT: 7decbdc7043495c59ecc64e720436bb0708b586a46f8745f74391477f5a2520905dfcebc3765a330999013d309dfaa997bf70bab6a0b8f4f2a2a3cdf\nTAG: 051ec4ecce208d9be0cd17f434e13be3\n\nKEY: 2d3d9ed4bc9eb9668733bafbb73e88be2cd17021c3a23be69b981d9f0df71df1\nNONCE: 84cae69639240c82b58895997511f145e474ebe1b008f391\nIN:\nAD: 64db597c26a4c3da\nCT:\nTAG: 2a22c4a962d46a719014ab7b0ffaf6d3\n\nKEY: 09ec4e79a02db53b19b54dd2d3592afc92c74ef57d1e0f51f3726a6631b1b73f\nNONCE: 2907ced16e0777fedb1e2de30df11b3fd712af41dd714a4b\nIN: b6e50cd4ea\nAD: b5488e9b7f339b7b\nCT: 0163e75330\nTAG: e29401c6d756adcc516580ae656852aa\n\nKEY: 9d5ac25a417b8a57b85332979e8a7cbad23617bb27772bbccc2acb0acae7b755\nNONCE: ff152421688dd6af7fef87817b508493a32d97a06fbda4f3\nIN: 92f4b9bc809be77e6a0d\nAD: 892b793f7a6e0727\nCT: bcc594f59de8ee8c22c6\nTAG: 1a8275816c0d32a1b6cfd41fa3889558\n\nKEY: eccf80c5f744d2ecc932f95ade0d9fe9327e19795023db1846d68d04720a2401\nNONCE: abc050fad8876589633b222d6a0f2e0bf709f73610aa23ee\nIN: 45a380e438405314510c166bac6840\nAD: c32c9a1ce6852046\nCT: 9fa452dc9ca04c16ff7bde9925e246\nTAG: 3d5e826162fa78de3fc043af26044a08\n\nKEY: b1912d6bc3cff47f0c3beccff85d7cd915b70ab88d0d3a8a59e994e1b0da8ac8\nNONCE: d8756090a42eea14ff25be890e66bfe4949fad498776ea20\nIN: e2f85df2ebcfa6045bd521abfe8af37fc88a0be1\nAD: 4576bb59b78032c8\nCT: 5eb6324aa48e0a4f72f5cb0a4917faf93af4209c\nTAG: 774f8077f039588495045fee07950e14\n\nKEY: 85162b111c9f3163f57c2cbc311a1e9aeed9dd6136b5784bc9c0b5052f8bffbd\nNONCE: 23cdb8b546bb8a5a746b24446f0ab4199f0543d915ff51f1\nIN: dc81000077d5743beef09ac91663885d984212bbccf3dbe6f3\nAD: 3084f3e9c4d0a15f\nCT: 692d17ae0b524ec6edc0cf49b69ac90c99bed44691f7ae63b7\nTAG: efe72ff84b3bccb4d83a27ddc574bc21\n\nKEY: b05ca358d8ca79f51283d83e2673bfb741c379ba271a773b8dd9c6a108e758d3\nNONCE: 9a53ad79f535c6e9da011463063c896f2ec7645e6e3548fc\nIN: 44e793742c774020e7349c996418042dc0dc30ee2bfd2654008c8929a436\nAD: 71ab5948c5e0f4c6\nCT: c5eddb7aeaa175b5f3dab68cf746f2acaf56fc62b29804629e25e2d63879\nTAG: bec3b7a8b8dad22ff3d14d26273294d2\n\nKEY: abb5136a01354c765a96e832df58bec3b088bd19dc4d6bd6674f2f02007ebdaa\nNONCE: 71267ac9f4fe5caa1d52cd85948a170a778f0141d54dbffe\nIN: afb526fe41c4e2a767ce77c4145b9d054268f5f3b279237dec97f8bc46f9d158868b86\nAD: 047baa2b04748b62\nCT: 0032d4c1e65da2266539464c5d3c2b1618454a6af0e7f1e3cfc87845c75f2f4ae8b03f\nTAG: b526a95a33f17ab61f2cdfc1e2dd486a\n\nKEY: bb826ed38008a0d7fb34c0c1a1a1149d2cad16b691d5129cc83f5eff2b3e5748\nNONCE: 4e02fe0915d81e9d5a62e5b3551b9db882e3873c0aaa230d\nIN: 20270d291a8d9791b0f5e35a64387bb4237bad61169841d7e1667c994ad49869c7d5580ffa752a2d\nAD: db852a275081e29b\nCT: d740012efb7e1bb986ce2c535134a45f658b92163c109bdecf1ce5b836879fe9e006a56be1fac8d7\nTAG: 21e931042e7df80695262198a06286c9\n\nKEY: 938d2c59f6f3e2e7316726537932372e05e8c1b5577aae0ee870bf712ff001ab\nNONCE: fb4d71cf7eb2f70df9759a64c76a36b75203f88bf64f4edb\nIN: 8910415d674a93c54c8f5e4aa88e59648d9a0a5039a66837d58ab14f0665a5f6d9af9b839f9033d0fe8bc58f19\nAD: a3fca278a63bf944\nCT: 1905c6987a702980b7f87f1ed2d3ae073abe1401b23434f3db43b5c37c979c2068ce9a92afedcdc218003848ea\nTAG: 1bd712f64777381f68be5ccc73f364a3\n\nKEY: dd0521842f498d23236692a22db0eb2f0f14fef57577e5fb194503e206b0973d\nNONCE: 519e0eee8f86c75c7a364e0905a5d10d82073e11b91083a5\nIN: 61ff13acb99c5a7fd1921ec787c8de23c1a712ff002b08cecc644a78c47341eab78e7680380c93c7d53d5e56ef050d6ff192\nAD: bb5c4e5ae8f7e461\nCT: 9bfdb0fd195fa5d37da3416b3b1e8f67bd2a456eb0317c02aabf9aac9d833a19bda299e6388e7b7119be235761477a34d49e\nTAG: 0f0c03b8423583cb8305a74f622fa1f9\n\nKEY: 189bd84be3fb02723539b29cf76d41507c8b85b7217777ee1fb8f84a24aa7fee\nNONCE: ef1bf39f22ba2edf86853505c24fafdf62c1a067963c63ba\nIN: d5f96e240b5dd77b9fb2bf11c154fcbff312a791c3eb0717684e4fd84bf943e788050b47e76c427f42f3e5344b2636091603ba3b1d7a91\nAD: 93368a8e0900c7b6\nCT: c55a8b7f587bee4f97514582c5115582abffd6312914d76c2568be6836f62ba098789ed897c9a7508a5dc214bf8c218664f29941ccdfd6\nTAG: 78f87352dcb1143038c95dc6e7352cfd\n\nKEY: 23a2dbfcd02d265805169fa86e6927c7d49c9a24d2707884e18955e32dafc542\nNONCE: 305c7851f46f23ea8d832d5ed09d266714fd14f82ba0f69c\nIN: 224de94a938d49cad46144e657e548bd86690a1b57b81558095eace59df1c552600dea389aaa609304fbc1eadf2241f2118c8bdf04522e1898efe1d4\nAD: 0075b20502bd29b2\nCT: 8e10c59369bbb0d72958100b05788498f59588795e075b8bce21d92d320206348b04010ced9b8cd3d651e825488915ce4a6e4f1af2f4d2f77b955376\nTAG: c39f0595ae8112dea6ef96df1c12458b\n\nKEY: 264e3c3f47bdf795cdde57d9a30be5a4da8b18463c0e3e05df28b7bf4e56410b\nNONCE: 3ee09b6e205c261bf48ac53a9ba0afa460a5d5c0f2d80be8\nIN:\nAD: 8eeec09d8972cb8ab0069554\nCT:\nTAG: 245a034d84edab9fa6f0decb6b984766\n\nKEY: d8ba98a272b5f91797b04b114311c3b92b7f2e3bb72edb7f78ed311b9f8ea2ad\nNONCE: 481de9a06eee76a501e3c2b9d7423d90596193ad9d8a6564\nIN: 9ee1a3134d\nAD: 928653701f6d6c8429b08c0d\nCT: 459a07898f\nTAG: 9188ec8d8e3bd91dcfda48fcc76773f7\n\nKEY: ac9afd627a745df682bb003517056f07876eb94d2f8c610c61b6ac0d34ec4ec0\nNONCE: eaae7b8704530db1e8c3dcc968a00604a333c7c27ba51b16\nIN: f7c3f6ee2e9c03394dc8\nAD: 796620b367d5f041821baf69\nCT: d4a69005790cc91d8d34\nTAG: e4c83def113afcf83a1ea8cb204a0eae\n\nKEY: ea1a07c1fd60a5421f1fb6c43b4318090e290c97aa3bfa037e6fc5ee00fd47d4\nNONCE: 37327805cce92b38a669affbca1de92e068727fcf6fbb09a\nIN: 7002ca765b91913ee719e7521ef5ac\nAD: 64e7c48fc3041eac0734737f\nCT: 9d8857a8c52a9ab3bf44b024b191b6\nTAG: d072c31714a7d0fe1596fd443a96e715\n\nKEY: b3beb34fe0229fc8f49b354e941025bde6a788f25017a60e8a49591ed5d7e7da\nNONCE: dd0e9fec76de1f6efb022b12164f7e9248b8e8c01d14ac02\nIN: acf360d7529a42be1f132f74745a940da9e823f2\nAD: 1489ca8d852f0a8547dbe8bc\nCT: 2e8718372d6e8167213cf112dc41c80377244f5a\nTAG: e4f31e8f84b9356999dc60989009e698\n\nKEY: 9357cecd10bab8d2e42ed88c0386204827c3b76e9e51150d09fd4e3b4e0e1e6f\nNONCE: 81f2106a5379e0ed861cf76b3cf95afb17515478b5cbcae9\nIN: ee51a0f25d091288b5e2b91ad11d491329e48b35a18a3a8685\nAD: b80cb677f4b409cd1537363b\nCT: f681f19fa8de1fdea3538001a46f30fa6333b76d6439337e68\nTAG: afad5e6d282d9df6d8119c32237b3e60\n\nKEY: 9f868600fbf81e40398b7dfb201fcae35d34bba10908860b0b2bf8b942b4e8fa\nNONCE: 2ddcc13c97185614095d437900b8c0a9170e0a4a50e46ba5\nIN: 133fa3ac176fee6df67472752e41c6834f13300c0064ff5b190f903b7ac7\nAD: 0d61321fbee8bb1f3f5cb454\nCT: b93abb311ec0bf018dc300c7d511b42ade72780373186e231820b44f22f0\nTAG: f8bd2f649a337783ff911e37966037bd\n\nKEY: 05affcdfce0a28539924370db8d80a78b835254778ec41acbff52bfab092fa33\nNONCE: 3edaeb185f7273b1a7cccba54f84c5f7d6583433b49d3694\nIN: 7657581faad266cc1037962a380c8aa5306f88000427d0a05397696b503790ad2643c6\nAD: d7c213e9e6f4a40f3e5b662c\nCT: 5eb19080aadc89f2329da4f5c41dc60568651c424c1b05d827f2bfb8dbff42c5a08224\nTAG: 2da20087b5674f0b967d1baa664bbd82\n\nKEY: 645ed60ec74ddfe1f02694792db4436c262d20405d8645cd9755d64876219799\nNONCE: d83665b44c1fdf567299f2b8501e9c0e7ae2dda0bb8f2c82\nIN: ceee69d32ad4667a00909964d9611bf34fd98be41ad7f0feaaaff8169060d64cf310c13bcb9394cf\nAD: 57379f8f44191ec9cf3b1a07\nCT: 4496a0666f0f895ebce224b448a04502f2ae7b354d868b7c54295bf051162e82c530c767d1ffd2cc\nTAG: 1ffc56da4fb961ffdfabe66d82ec8f29\n\nKEY: 06624c9a75bb7dbe224a3f23791281f53c40b407a14161a3f82f34924623dc02\nNONCE: e647b8b4739bf542a81d72d695e1cd6ba348fa593987ac47\nIN: 2658763f8d70e8c3303582d66ba3d736ce9d407e9507f6c6627e382d0144da157d73d0aee10ef034083cdd9013\nAD: 75536443a6c2189a57d553bb\nCT: 305cab5c2f9a6edccac307d6965febe3c86f2a1e31ac8c74e88924a10c2a29106bce980c803b7886985bba8ec5\nTAG: 8c12bb58c84175b9f601b704d0f8a25c\n\nKEY: 63aeb46083100bbcc430f4f09bcc34410df9cfd5883d629e4af8645ffabb89c2\nNONCE: b09830874dc549195a5d6da93b9dcc12aa1ec8af201c96bd\nIN: 1b3c9050e0a062f5a5cff7bec8706864c",
     "f8648142ec5cb1f9867ace384e9b2bba33aab8dc83e83b2d2fac70cd5189f2b5ab5\nAD: 7dcc05b0940198bd5c68cdf1\nCT: d8b22e5d381de08a50b163c00dbbca6c07d61c80199cebd52234c7bd4f7ed0a90d47ef05617cdb8e3f782875ae629c0f0ad6\nTAG: 194077f0e6d415bf7307d171e8484a9c\n\nKEY: 4826c1bf8b48088fece4008922173c500ff45790f945b1027f36110da4fecc92\nNONCE: 3a78fc7397944d762303b0a75974ac92a60e250bf112600a\nIN: d26e3a2b92120ff8056bb992660cc8a2364792589c16a518b8d232b8184aed05ba8d4fd0b2ad2b928cd873e11905a21ffece5f1e63c974\nAD: 904d2cd3e50f7bfb9352f142\nCT: 21f4cf679662fad36f57945fc0c0753c3791261eb58d643278dfe1f14bfb585c5a01370ba96f18dc3f6b6945a2c6997330b24f12f5219a\nTAG: 95397c54428f9d069c511b5c82e0151c\n\nKEY: ec526c03d8a08e8a63751112428a76399c399e8b83d98c9247c73164805ac8fe\nNONCE: 2cc1a6ae89c2a091415fa2964b44a0e5da629d40d77b77f1\nIN: 567377f5b6df5442e70bc9a31bc450bd4febfcf89d7ca611353c7e612d8b7e36e859f6365ec7e5e99e9e0e882532666dd7203d06f6e25439ed871237\nAD: 35575b56716868b66cd21e24\nCT: 6b738274fe974438f1f5fca8ef1ee7df664f1e72bc54ccd3fb58c4a3df67ef9a73261df41ffe9c52aeafc8be4f6524baf9efb1558d4a57defec7bee3\nTAG: 92599d4b14a795e8c375ec2a8960b4dc\n\n",
 };
-static const size_t kLen18 = 96825;
+static const size_t kLen18 = 96824;
 
 static const char *kData18[] = {
     "# RC4 tests (from rc4test)\nCipher = RC4\nKey = 0123456789abcdef0123456789abcdef\nPlaintext = 0123456789abcdef\nCiphertext = 75b7878099e0c596\n\nCipher = RC4\nKey = 0123456789abcdef0123456789abcdef\nPlaintext = 0000000000000000\nCiphertext = 7494c2e7104b0879\n\nCipher = RC4\nKey = 00000000000000000000000000000000\nPlaintext = 0000000000000000\nCiphertext = de188941a3375d3a\n\nCipher = RC4\nKey = ef012345ef012345ef012345ef012345\nPlaintext = 0000000000000000000000000000000000000000\nCiphertext = d6a141a7ec3c38dfbd615a1162e1c7ba36b67858\n\nCipher = RC4\nKey = 0123456789abcdef0123456789abcdef\nPlaintext = 123456789ABCDEF0123456789ABCDEF0123456789ABCDEF012345678\nCiphertext = 66a0949f8af7d6891f7f832ba833c00c892ebe30143ce28740011ecf\n\nCipher = RC4\nKey = ef012345ef012345ef012345ef012345\nPlaintext = 00000000000000000000\nCiphertext = d6a141a7ec3c38dfbd61\n\nCipher = RC4\nKey = ef012345ef012345ef012345ef012345\nPlaintext =\nCiphertext =\n\n\n# DES EDE3 ECB tests\nCipher = DES-EDE3\nKey = 2eaf97304cfaeb822c04a7b7bef328c7b82fef2ae81b06b5\nPlaintext = b3ed255d4f5e2d6d9a1aa2bc03489064d28fe1431eceee183b7231fad3273140\nCiphertext = 629d608789e51dff699343d061ec01d94c0681c7698ab617ea0145f37304c8e2\n\nCipher = DES-EDE3\nKey = 72f52e1ded0a88eac6c88d2901b27c2fd6e9f3f3387432ee\nPlaintext = 60de7b5667a1ad5995f178553d695d9b0fb537562876faa9b6cc50d05a1271ac\nCiphertext = 09875b215ed2499318c2d99c48209ca5f288830cc6edb9538190fa3ca31fa175\n\nCipher = DES-EDE3\nKey = 4cd30f1e14d485dbc05c69b65ebf44e556056a2261c9e714\nPlaintext = 7ae59441bbd665d8867273386fc72a8cd62cc5bc6bb7cbd57fc1f5dcdc73adfd\nCiphertext = 004d0daad970bfee944c8779927867b964dd0afaae7a830c2b8e7fdcaeac2158\n\nCipher = DES-EDE3\nKey = 7a07ac63adfaf1b26860ce39edfc402758bc4d1edda156ab\nPlaintext = ed4f18cf10bfcbd4354710df053d3e2b776860137349471b8dcf526b8eea8b22\nCiphertext = a1fc1014abda7e198ac8e096a368f65d9b59e1eddf5d97715015a2408dd8e799\n\nCipher = DES-EDE3\nKey = 37c443ab336fed59b0efae51ea7f5d07b7040868beefcd99\nPlaintext = a10a180cc94b75fc9d6556dfc0a816b71020dc3906f9d59d9dd4c839dfc1c1d3\nCiphertext = 0a874652b621618635138a7d4b33bb624f91a39ef1422b3e0490311ac6df3602\n\nCipher = DES-EDE3\nKey = 1ff1a7f4839d484e308d9b8c2c052b126def413d5fb8e0c5\nPlaintext = 4f28ef6683d36c80556ff240b247a3967aec23f859e3afb93aefad93b1e9964b\nCiphertext = 87a8f3fb4c51b3caf19c4ac51363d92025acc053e538c1502d347a618314a4bd\n\nCipher = DES-EDE3\nKey = 5642c4d1859a85b342e3f253fd8bd835e856c451e63673e5\nPlaintext = 687af9b298db752b47982f64ad9bff52a9ae487aa5e5c08f902035b0633225bf\nCiphertext = ca2ed48392ba5d70879ac8772180c3028ef946b6ac1df0348f206ce16bc449bd\n\nCipher = DES-EDE3\nKey = 30f9d27472f9deee309dbca76ba29ca174c39d0631084735\nPlaintext = 85db2c266902932c8e46d0207459b203f90955adcd7506b49bc82e2796de764f\nCiphertext = bbb5940b45add7c587cc9fcfc40674bac7e081baf71285891c65ed9573947a07\n\nCipher = DES-EDE3\nKey = ebc5a73005b77a812c3f4f61669ba859939852580fa61cbc\nPlaintext = 808d22c60b883a986dcb0860e8d92a75441cca0a2a4b06dd78dbcbec198b38d9\nCiphertext = 75d39d30862431ab07227e22b4c8218f1fbc2a3816daebc555c1b999c86d15c9\n\nCipher = DES-EDE3\nKey = 2e8eb05dd8a2b7a5a61a6b8a3830b12da2c4b1bea1e884d5\nPlaintext = cc7569d005afd1a365f5c5836c14475fc15091199902af4a78460d56c16f91ca\nCiphertext = 64db8af7a30363051a017cc92ed67ac6c0e2e1ffda0c94bbf0eeb803ba6b3d22\n\nCipher = DES-EDE3\nKey = 2e8eb05dd8a2b7a5a61a6b8a3830b12da2c4b1bea1e884d5\nPlaintext =\nCiphertext =\n\n\n# DES EDE3 CBC tests (from destest)\nCipher = DES-EDE3-CBC\nKey = 0123456789abcdeff1e0d3c2b5a49786fedcba9876543210\nIV = fedcba9876543210\nPlaintext = 37363534333231204E6F77206973207468652074696D6520666F722000000000\nCiphertext = 3FE301C962AC01D02213763C1CBD4CDC799657C064ECF5D41C673812CFDE9675\n\nCipher = DES-EDE3-CBC\nKey = 0123456789abcdeff1e0d3c2b5a49786fedcba9876543210\nIV = fedcba9876543210\nPlaintext =\nCiphertext =\n\n\n# DES EDE CBC tests\nCipher = DES-EDE-CBC\nKey = 0123456789abcdeff1e0d3c2b5a49786\nIV = fedcba9876543210\nPlaintext = 37363534333231204E6F77206973207468652074696D6520666F722000000000\nCiphertext = 7948C0DA4FE91CD815DCA96DBC9B60A857EB954F4DEB08EB98722642AE69257B\n\nCipher = DES-EDE-CBC\nKey = 0123456789abcdeff1e0d3c2b5a49786\nIV = fedcba9876543210\nPlaintext =\nCiphertext =\n\n\n# DES EDE tests\nCipher = DES-EDE\nKey = 0123456789abcdeff1e0d3c2b5a49786\nIV = fedcba9876543210\nPlaintext = 37363534333231204E6F77206973207468652074696D6520666F722000000000\nCiphertext = 22E889402E28422F8167AD279D90A566DA75B734E12C671FC2669AECB3E4FE8F\n\nCipher = DES-EDE\nKey = 0123456789abcdeff1e0d3c2b5a49786\nIV = fedcba9876543210\nPlaintext =\nCiphertext =\n\n\n# AES 128 ECB tests (from FIPS-197 test vectors, encrypt)\nCipher = AES-128-ECB\nKey = 000102030405060708090A0B0C0D0E0F\nPlaintext = 00112233445566778899AABBCCDDEEFF\nCiphertext = 69C4E0D86A7B0430D8CDB78070B4C55A\n\nCipher = AES-128-ECB\nKey = 000102030405060708090A0B0C0D0E0F\nPlaintext =\nCiphertext =\n\n\n# AES 256 ECB tests (from FIPS-197 test vectors, encrypt)\nCipher = AES-256-ECB\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nPlaintext = 00112233445566778899AABBCCDDEEFF\nCiphertext = 8EA2B7CA516745BFEAFC49904B496089\n\nCipher = AES-256-ECB\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nPlaintext =\nCiphertext =\n\n\n# AES tests from NIST document SP800-38A\n# For all ECB encrypts and decrypts, the transformed sequence is\n#   AES-bits-ECB:key::plaintext:ciphertext:encdec\n# ECB-AES128.Encrypt and ECB-AES128.Decrypt\nCipher = AES-128-ECB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = 3AD77BB40D7A3660A89ECAF32466EF97\n\nCipher = AES-128-ECB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = F5D3D58503B9699DE785895A96FDBAAF\n\nCipher = AES-128-ECB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 43B1CD7F598ECE23881B00E3ED030688\n\nCipher = AES-128-ECB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 7B0C785E27E8AD3F8223207104725DD4\n\n\n# ECB-AES256.Encrypt and ECB-AES256.Decrypt\nCipher = AES-256-ECB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = F3EED1BDB5D2A03C064B5A7E3DB181F8\n\nCipher = AES-256-ECB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = 591CCB10D410ED26DC5BA74A31362870\n\nCipher = AES-256-ECB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = B6ED21B99CA6F4F9F153E7B1BEAFED1D\n\nCipher = AES-256-ECB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 23304B7A39F9F3FF067D8D8F9E24ECC7\n\n\n# For all CBC encrypts and decrypts, the transformed sequence is\n#   AES-bits-CBC:key:IV/ciphertext':plaintext:ciphertext:encdec\n# CBC-AES128.Encrypt and CBC-AES128.Decrypt\nCipher = AES-128-CBC\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = 7649ABAC8119B246CEE98E9B12E9197D\n\nCipher = AES-128-CBC\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 7649ABAC8119B246CEE98E9B12E9197D\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = 5086CB9B507219EE95DB113A917678B2\n\nCipher = AES-128-CBC\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 5086CB9B507219EE95DB113A917678B2\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 73BED6B8E3C1743B7116E69E22229516\n\nCipher = AES-128-CBC\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 73BED6B8E3C1743B7116E69E22229516\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 3FF1CAA1681FAC09120ECA307586E1A7\n\nCipher = AES-128-CBC\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 73BED6B8E3C1743B7116E69E22229516\nPlaintext =\nCiphertext =\n\n\n# CBC-AES256.Encrypt and CBC-AES256.Decrypt\nCipher = AES-256-CBC\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = F58C4C04D6E5F1BA779EABFB5F7BFBD6\n\nCipher = AES-256-CBC\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = F58C4C04D6E5F1BA779EABFB5F7BFBD6\nPlaintext = AE2D8A571E03AC",
-    "9C9EB76FAC45AF8E51\nCiphertext = 9CFC4E967EDB808D679F777BC6702C7D\n\nCipher = AES-256-CBC\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 9CFC4E967EDB808D679F777BC6702C7D\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 39F23369A9D9BACFA530E26304231461\n\nCipher = AES-256-CBC\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 39F23369A9D9BACFA530E26304231461\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = B2EB05E2C39BE9FCDA6C19078C6A9D1B\n\nCipher = AES-256-CBC\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 39F23369A9D9BACFA530E26304231461\nPlaintext =\nCiphertext =\n\n\n# AES Counter test vectors from RFC 3686\nCipher = AES-128-CTR\nKey = AE6852F8121067CC4BF7A5765577F39E\nIV = 00000030000000000000000000000001\nPlaintext = 53696E676C6520626C6F636B206D7367\nCiphertext = E4095D4FB7A7B3792D6175A3261311B8\n\nCipher = AES-128-CTR\nKey = 7E24067817FAE0D743D6CE1F32539163\nIV = 006CB6DBC0543B59DA48D90B00000001\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nCiphertext = 5104A106168A72D9790D41EE8EDAD388EB2E1EFC46DA57C8FCE630DF9141BE28\n\nCipher = AES-128-CTR\nKey = 7691BE035E5020A8AC6E618529F9A0DC\nIV = 00E0017B27777F3F4A1786F000000001\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223\nCiphertext = C1CF48A89F2FFDD9CF4652E9EFDB72D74540A42BDE6D7836D59A5CEAAEF3105325B2072F\n\nCipher = AES-256-CTR\nKey = 776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104\nIV = 00000060DB5672C97AA8F0B200000001\nPlaintext = 53696E676C6520626C6F636B206D7367\nCiphertext = 145AD01DBF824EC7560863DC71E3E0C0\n\nCipher = AES-256-CTR\nKey = 776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104\nIV = 00000060DB5672C97AA8F0B200000001\nPlaintext =\nCiphertext =\n\nCipher = AES-256-CTR\nKey = F6D66D6BD52D59BB0796365879EFF886C66DD51A5B6A99744B50590C87A23884\nIV = 00FAAC24C1585EF15A43D87500000001\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nCiphertext = F05E231B3894612C49EE000B804EB2A9B8306B508F839D6A5530831D9344AF1C\n\nCipher = AES-256-CTR\nKey = FF7A617CE69148E4F1726E2F43581DE2AA62D9F805532EDFF1EED687FB54153D\nIV = 001CC5B751A51D70A1C1114800000001\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223\nCiphertext = EB6C52821D0BBBF7CE7594462ACA4FAAB407DF866569FD07F48CC0B583D6071F1EC0E6B8\n\nCipher = AES-256-CTR\nKey = FF7A617CE69148E4F1726E2F43581DE2AA62D9F805532EDFF1EED687FB54153D\nIV = 001CC5B751A51D70A1C1114800000001\nPlaintext =\nCiphertext =\n\n# Regression test for https://github.com/openssl/openssl/issues/1916.\nCipher = AES-128-CTR\nKey = 7E24067817FAE0D743D6CE1F32539163\nIV = 00000000000000007FFFFFFFFFFFFFFF\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nCiphertext = A2D459477E6432BD74184B1B5370D2243CDC202BC43583B2A55D288CDBBD1E03\n\n\n# AES GCM test vectors from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext =\nCiphertext =\nAAD =\nTag = 58e2fccefa7e3061367f1d57a4e7455a\n\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 00000000000000000000000000000000\nCiphertext = 0388dace60b6a392f328c2b971b2fe78\nAAD =\nTag = ab6e47d42cec13bdf53a67b21257bddf\n\nCipher = AES-128-GCM\nKey = feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbaddecaf888\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255\nCiphertext = 42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985\nAAD =\nTag = 4d5c2af327cd64a62cf35abd2ba6fab4\n\nCipher = AES-128-GCM\nKey = feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbaddecaf888\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 5bc94fbc3221a5db94fae95ae7121a47\n\nCipher = AES-128-GCM\nKey = feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbad\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 3612d2e79e3b0785561be14aaca2fccb\n\nCipher = AES-128-GCM\nKey = feffe9928665731c6d6a8f9467308308\nIV = 9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 619cc5aefffe0bfa462af43c1699d050\n\nCipher = AES-128-GCM\nKey = 3de7b368783bd7287f2b9b731814c876\nIV = 90dedcfff100eb1f1db9d935\nPlaintext = 8d766795cadc0961c0f448c62df3827eef3a8664599b3adbaab0cfd63875bceb8f992b4f7447dca10ddd716aa0bc4fe925e1aa3e3fd1d5c430c650fe3546d6b9a24d576a857c5f04e8c0a3b149df277aa19cfa64ee235891d3b8ec0e840d268b1e70dd8a4bf97628a0c7aea38aa21eeb8fb1a8437f2abfee05e0d2c30659e312ec03d30da51b7c19073a2341c17df806e27e796d581143d39e4de8d3f8d46aa6d6fc1a98d94fa69b92dab751d930cc12de21fb1a7468af09e3c12ff6c3db3967d10cf140bc46f17a16e24b010b6cba5ebf777341c52042596ee53008389c48d9690ab9f5625795c3e588f72f7a1670b2b25a9f4eee1c8845ac90f1bf47ae4ea4b607a50aca88ed304cbb700d02d5486139b0bc81ec042e574abf986972fa008b83ef22dbfe720c2f2f6355c87c975932cec545ebed657e5e7570c503e9aa7f0b87d0b2648e421ed1d34749637c95d1e931af8925236387e50454f0ba2e22ed05f90450fad46f4eb7ddb08656511dd065c0f852a7e42f618a961a6c6bec42226c6b6043580b009ec9837cf99844cb74794a82c269ff648e0bae9ae50256a0ad98ad9f5a35057b3004ac96f469f9ee966dadc16dc47616586cf242706df96bb2f7ee43d3bd1c65d2eac7b82ef242e77ab509afb9639e5f3995380e926305729ca762c487f4411ec2a9c688b8347e5287216dbb38c3fe2281a89fcb47ee2ee7ddf79bfa3ab61cd56a00981019bbcea8aa0444eb75958e5fa56ea0036d2de4950a7db886f4a318b433bd41e00905ab158171e0ef13172293bdf70064b9dc7b243bf9dc927589bf9e99468d1cb330639dbff1850cc51929b8971b0b2ede9d06bc5f6ba39d4551b587f09bf6f8206e8f1524f55714612581d6aa45d8fb83425f84a736576deeecafdcbfbb8670d14cd2ab2a7f8b7f374c07881b7bac2605fd5ff7ff7cf43e30cf49910961a9079c0343b8601be8c3e9fe38f49fdab0b7e1a8c1536cf84e4d80d26ae5ec37570839b5cda02929221898d611525c3a88fc444167ffc532b256cdd0a8f31ff08097d75b629fab99c9e1062d1d9962b211e15ec8709934029c4934e64db8d7a2f32e23dc541be306e9a57a3419115994cbc3a8f8d5ea2a6f45b9ea9ac0e51ed0c6680fa029f4552a6c8665aab00ab77928342e7284c321e9500ad4774ef1fed0f596d5aea371fe1793271aef38cde55547f34701a525526e83a72673385a85f44db511bc87ce1f831fc6ccf8204ca4f4a20eac09897aae93684f14ede21bcaf40a09c08012b92600d6a839ebdf8bdca7b34192c6c50bad8796b3be3c375dbae6217815d2c75cc878d39b4e842d4eaa5f5df2242cf230e44a240e18e47827f089b18bf880fd41a2516eac8e6ba3fc2db64a4bc28789860d7b18d9edeae8b3059f4d945b15d0ee27b1f74842dd1df117fe83a8fdade23a47c93902eedc4d33f2dbfcd1996e6dc1458409fde2302830e8d44c58c5ae67486b9950dd938f14c38bc4c9484fdc4ded93a0f90875773453fc14d428cd6e7beb0c705d61229d2b3df09632ebb30b325fefe2aebbf2a7aa8e4ad46277ca4b8b078818b63d04e7652057f6cbbab7c43ac355537e0d3918b4a73c00dbe6b30a27ee7a6fa213d3347ae478e8edc323404b8322b9c7b0173ed61c38ed25f3576a675d527d22edd51d6dfa5767560d3a50a91226338e8c4e6436eedbcd3d2efe9dc1e686b15d2f57d553abcfda57dc316ca453a690f20148f0dfa20c1c4a58240aaf7195095fedfa56d839d0230d55ce9a8ca1b9d1acd6fe98d583148ba0f4a4e3413c76e6ec57ddb79428d3a90079f64d3321c791f60d501c3fd02c8403f0f5e6c6836bbc96430c1b48e83350c3a3cfd017f15bee3e4bb1295d821dc98b85ab3145555cce2c34a8142fe50f8db19918b514a165d12ff6301fb2296788760ac0b6d9e3a57770ad5111cde5d24b6321918cb0b0887a282b827a8749733171914b000e7d3c0edad1d42ca60da37f0698554bb2a1749f73b3120dbeaa32951f8217a781a200467d",
-    "5b569d16f56fc9b7dff0ac524f03fee0617f4c692d94613b1e13b18075dc9f0d32811d4a8949a95f6b5fa46aeb83597adb409e68b2a0177c36dcc95dcb2e7dd4fb7337ff97c013364fe139e185014948fa698741d822044fa3f6978b16afd18138c845587c405ebf7a6cd1c28610ce67e992ed49e406658a0a202feed9709500d064b6f53eecfca57dd4b38363ce3aae9d59126d8ae7e140a373851188ae28c909181d0ac64770df70dd2475809350cb367825b59d521d5e457b4e36aea6dedd90a2266898b753b57fa359d43cd388e7d6c7ed90bc4c2af34ceafe88a3af6ac376fec35f1240f08af4f3eb30bc53dd68e5762e6d39e6b16f63003fbe0bee828d0d7adc58c41e857c2c44702215b202701fc696eae021af19c79e59c3e32627cd571f5db99b17f1772b5d746196befabb0b7446687827f3315b391d5dff069b1c39c00bb143218ef458e3b397e1c99640d57fc8db2e0083d3d22ed4111a8fc9e0e6f55fe6a56e946dbee43909bdd7d516fdf756ed8099ba80b1e17a5e279119345104379a36962ca9c8b2a53c414d79eb09fe79862ca749a9eabd9185ad1df57215945882f5894868a134bfc35c835e040e77ecf077d6a98a73ee022963d70b036be3fe5718280ae52c5d751211b22950c0597aaedd35af41f7dd5999e5f7ee34a37edcf97df54a46742b0252b196eaee454ff0c30685b15f8de087de208906be1d971f0fd89f7cdff2af0bdc96759d6889fba9ef092ad1c8deab0404562a7f3977d211c28dfd1573aebd5427a8773f03986101703fa19cd4ab96a381c76a747f63b63f7a9a3a08e251cdc593a024f63b443b76d17dd9e151809da3c582fbd334fa6dd0221b6d410c6a78ba95bb0154bb8999f619f2e084a6b9755ceee4ca3c7e0481a47776c8814f13054e627e37630d593bd09d5f10a049c66c9999f4b0b037e81ef70615d674c7c7975972994a053c069675fad3fae5ae3e779233b70254fb87f25d44c104afc3d5911b8b695173f9337130e39a02cf97356cb817f6cd23f55ef74dd06bd24ce5887a7001ef576262ffaa99f9bb5e3f55bda2aa0f199115909af48bb4d6b1a0a0847774515302cafebe75aad1f63362b1f38141e8721851c3ef1a247931b3b450581eb5d09027b9e3ba60ae9801d629b74991b7fd65520eac561d47115a85141d9a757bc75710bedff1630561ae05254ea541a7ff1846ed5e164834417556dd562c45543c88d8030bb56451fd5b3cbf10fb0164c5288789d2aac7e7a836e79bc3dd401a8e3e05aa6714ffb2dfddb3037c35fa1ebed62a073b2da42133f2620ae88de5e3f46cc69f2b9b3c9b88e39b8b108059ac6bd493be5f7a39f6b53ee825f4593b77ec9238f5ab804d533f48803e7d8187291ee25cfac4da5d8c9279517adfb09c422f6d704711726c73828a5082b4c7b3d85611b8f496d3e0f78c5c4f1dd1c722b1b11d55861f232beee6aaef8a00fd2eeeb45f182af191ca6de8eaa25ceda5451416fbf6d1abc0670b8c10e2815076f271044c690bdcb64856b91265bac202043a28f6bbeb807535aad4bd89e572a9427c826b170d3862f4cca70ddffb4769d6593a1cc6c42fd06cf68642835fe474a23e6f63df316f8361bab959b768d78e20c03c2a99913c162a9662bd9981eee55922f36792de0af68da04ab49dca72e3d9b0de79df828b433bcf6be073f851a36418c03a717d54d48c1014ccb793577c8393b7cb53cad6bc7060a54cc6363734f6ad388763519ca09b533078d3cfa61d7bdd4c4dd0ffe64d68d501b55903d3f4a1f310a3826ac2ca700de01d656188dcf577fd1b63e305614b8d13471f6f84a5d4b12c5e119870a63d1e3dbd39d3b5c26b09f9d80f8a59ce836b20bc933496923d278a022c00f3aac204d07d2e5075bbcef1e4820d633a3a2b35974f72a033484a91a1d6a9913239c93e5783b01833073c98f358e3465efd5087af37ad60b7285550e776d67ea7019e788776c5a456102358c32eb4e7c28096af88b9a20d8ce379ba3928a10ffd539c106f4927e7ac0f382c74017d6e4438fb128c660affd45e9bb68452de72b574eeffe3ce239d0718908c3800bc7e8ecd2fc7d9754171506017fd7868594c9373a96579fed475a28811649ce5dc8a3107bd0d8578748878ce4998684620931dc3981a2499568c2f61174c3b3fc46a7010468e8ff75c08cd43ac764d95e2ad1659f9db62e9554f811e0f43bb74779d923c8c243d12a5314d3c0c6ec84fe60e1d2b2e2b20d3e64054d62049ef9233ff55223a319c285e4e3f4c98dc95b2ca81230d7fed9bb99fd7d97430eb32c9c11647992bd85dcb47cfd58ea3e221d095bcf9374a6baa7c8333581f62b9e489282483023fdd18451f09bec764146b587209160b3d1d7a3d2e145fdb640c4bc382541e0d84255122d51a710887ebe1ccf29d41b4dd7fd7368d68ada250d3968d6f0971f0849c13c09abadb9db8b08960a18f84f0346ea0aa71227afa55b90cabc062d549b616400d36450b19adb67d7358e48c043fa1135abfca89374c906f8d1a6a845debf6b37f055d390b029c7f4524958bdf8d7e2755dde3b957f0926f9d3b8821ba96044d3cad2d637b973bfb657fcc06ff44c17965acf572ab7a0c87604c7dd1cfd136a0ad02b22e8ef320e101ea09772588e8c5b4d88f40fe1be18d27146a2b9559491949671700cebff9a709f297c2621ca9d5d1749623abc20a326ff5be55cb9435c03bf49b147b1e0a4a918bfdc3642df90b396a474f81d75c953d87b3f3b4e31fced630bd7c481c63acbb84dd31249101ac5277a36dcdfc80d8d9a2e928e9b2d65bb257bce97ccda83b187da8a7886dc96eab93d0864d88c358105f9cfe1ad0f0a8508b5b3985ff95de652e684da970b57669aa3fdfbe590a631522abe8246393639709a9a6cd549e78e3c2d1acf84643e9f554c5e076f75a5c1dce1be20a66722d0b896837b7036509ab8d473d5d2b7a8374d6a575f69d54afe3e7e18f4faf4e917be8a74e55c271b96d966e0c0b883f84b3ef2e4f278daeda2efd3ce770801d2c4bda5eb9b646deeab9fa55324e917e63e4eb6aeb4176cb4e43af3db61aea1546fbf16e76a12fcdbe726b565710e3f9866551023e5fbac0038678717e6ab4d3e92dcc53049e8cb65c00216d31a8869ff4d3539313fe2fd7ce0f53b255e3659e7dfc5f92b7627dd9ba42972f0ba72b888932d870ab97226040c4c0f4826be131fe1d2cdc21005ec2addd7796f0927501251ab26b0e5f3f9d2a1cb346a774e18bc233cc89aa69f5f70e3d5c17098eed350ec419c82837153b5c7f5813bf5918defc8df143063f3fe45125deded2b15892d5cebce589b60f2ada0f9d608983e8d107d8e6482b5f542c6650b014445e8c055aac142f16cfc59229fc9626f7aaa40cefacef777e494e13dfa93d27c201788ca9f60e572af8d65ffb513473dade5fe494cbf7377bd1ed03db2571d65af3be4b0bf27c1f069797bfb67ef0bd8a88c6286af6712c106df9c418d88054e3b46c88296a2e63894d6bee0dda8833c373d6a1b27637e1510fea3eb2fb34ae27354571369653a282a8d19f2c34f9e5ec34555b4ed24327dc5d246df13736bd41021697104f80c85bd0ae920e9aeb4e628fb8aec269d55858df149af298b06d61250b043c8a14a15f0646d0aaa18109d031c449e66dd7336044dbdec912b1bb615fae2a3df480bd64cbed74be65c8f1acac247e80bbaeb6f9dab38c6addf4f3b094d5934ef5c9749053b9159e280034e601731a12d6688ff27ee3581ae289de424d16676fb750d2ccd5b3f964dd77bdefc15bb204e2350632822384cc194cf9130f1ee81bfc3887d3366ec0b48cbbe0fe674281ae7445f03791887873659825680448f162452cef57d783821a73047078a8cf94c416850092ac772ef0b2e48517ef101ee0681b5259aa27fd56edf3c01e6dba6298ccc91b09bb304b637eccf8c673b816e74bd7f8ceffa6b17ab03df7ee9ca4098d24d044015a07df782a309cb6761528272632a6e1323c4e18284b463dfcabed708e4fc95cef133865cdbec8bfdde100621c65a92762cc3141ff37b66dea8fa6e3aad61dcbf3b512467c4773d36e58989e12a636389c1678c191137a5f7f59668c8a527dddcdd0c3fbb14cf48b8f3ea306850a5eda76c57aad06312d7bbfc18969d7b611f512358a7bdf959cc2f41de1c408133ef02b1fb2cdf8efe9973c27536434e56fc1bb4880db7fe901087b53ef3c0de18aafa47c25f1cd62c362f2e5da41c2dbff0e13adaba26c1e0829f027dc0320442e851eaed9507b70ac17180725349f6ea7b59bf39c095a9d10790e87221c7c2d24b8bca184ee95a3ef7449aad6c1d905f688498ae7a0cd1b01f76dabc342fb2be0295ca1484bece3c9b8a1b91e53de2d2587f3607a7f348f5cbefaa7a6dcf61bbbcae9444e2d25a77b016cbd1508c8cd319e9812b43b0bbca52df155d418dacb6ab1360a9e605fb53c6e20588a10bef42d884989e836b2ff16fbcdd2c1704f75dc8c1ac2cc6aeb92726f5d46e4784c70e1e249c102be6da506e5e3c2cef6a8bc4a60dac7adf3cacca8679f8f792ddc27613e44a70fd849b7617e042da46d65a3e6cf425f59b83cbae5b6e911142abd13a0a8cdf06d041435ee20e2ca417e905d2dc49c15b863ae5920ff7f9380a86bb0c86b69a000c157cd35245bf71f9dfdefbd1760af90ec3e554ebc511aebf650633221ca9157226f613f41406872765f8d7b916ff3877266f017b8d840dca0697ec3dffce7912ea9eafb62cc2f2d0a112c9bc0727444b47b62766bddf5b5f26d391f653b6894b069069979d0cf8cc7fc4143626a8420bc0a3866db3860096cc128d620ceff059d1614487004adbdf6b0c4428ac8897dcf16e6b11a692a6b465a92b40010f3480b444d4d2e24b0af8467666905c2a6233bdd6502521b621d3cdd4a5e1f268d65bf6a1879608ffd3abf635c5f0948f3cec7e087485c72b00258ba69783cfe7d611bc41c27814ef5674185791dbe626e1f276cf2c399a4eb264f19c77ee95d94252f546528f629188318e9ede65a927aafd2f2af56ff32c0ef39862d2f92268bc9400afa8ddeff591f3ef99681263a33b873bd9e01a59c8b281da30875245cbffee5268563c7f6f20b9e22d998934131dd219624d3cef6df2f3d2d6401833f72c619d6f763837141dbf93179d0f01375581ebe227185166aa7988eb9fd453d510ca6616cc013d551d23a33a4241e85aac3201284344977d496d768f5d920c5670b1d8bb608efc1b99abd261afb0a4ebe191605cc5c2e20523a13b3b94dd1fb24a27009d9a5b6329336f3516a327642386ba64c8769da1324a8a3d1f304cf0700df2b3e38215a954523e1d40ae96d0046e2929a815bf70785e94bc9b89246ab6aded60d65170eeb49b0ee0a57ee2e57db92409105c25f2d0c1a17b5556d06511bd0991a426258372c7f2b402dd533a75aa175524eb5d6b9575300b81fdb2258bd74429add8aa477bd1182db57107d411d16147defc3582861c68f5ce82e0a0316edd5d0f3cf36825a2c79a33e376cce2e63274b3b41bcbdd755845ad9ed2a3bdacb6fa3fa9484b7b60edeb1d9ef84772e78e39adca14c9fa0bb3ad1f1c17fb9449270e9b4c97b5b320839947fc73853fc58304ee9c9e86f3775f5469554d5006eb7ce9d02d5f900c771806c275ee7022e2b55d111338dd93ad51d14008df4c13d8c03fd9bb3689607e5cbdd499c3a372b487af74cb140f6300cd2dc2acda07277ea3dab57ecf0",
-    "9f1a8f2d6abf7c44fcdaa6dcb1f6e791164004b20b3b4c860f409c1483c7044b6fa445f7224606894e386ba08057a387b48920d4de203b1acc4dbe2b0b4cbdc3f7d7bbb097abbf81e01db09e120eab83def925a059cdb513efe6bc93f0579ebf75638df3c3d7f9eba3c36a169e9d88495c452888853640d93ee70f254f86e2d2d3fbb5e8883b36fbd2da105cf3a75cfe998068203186bb37f1d1ebead8ce1f9383b816f1da2fd0a9e01377b6ebfed4f05bec08b4ff9b90e385736fd13a3af7980c21b0dab58decea8e9545af5d0fb11bb51aeda2c8616960e8f6f84e6c2fc4f50d7e413afe030f75475509fbcf49cbe14445d267994fd3f38f41a1339f2895c0b2969a9bf9c59b85e629486c7bb5107c7a6b069793be7690f7a7c96c93b09a9d610594a156ab27a32d5557a5b1ec8920761cd2f559ad808dff3da64717ea5f10fba87b8ff2712ce322eb3c288939e0007f779a3920f45fdd533369f6f85a8cce21f91552fe03702ef81a926af0e402b418fbb25a6a3dad0ec18ec663126b3f48c341e2725abfeae865352d5ad275a9e3ca20393c64d118968023daac84bdc724a3c522d97a5878ed788cf8e44f80f8803d57584d8c8688cff24dd8c0e881b62d16ea30104d62007a4bec051da7fdc95d1df8556ebdf607383a0825ae503e24661ceb8ba773b793360c3f4ed3b761bd372570cb17e7c2030f07b0b45a7974e45ee6fcf5bd7ae9e9abde5421b42cff6af0c6eb7fc73f4deb67bb4e0b3dc9b4008da30c67071243cda649091a14b89bdacf2ae98dd230e932d9b277d6968c65e0006a8ff63f283f2cd9c21615dfd82e0b24af6ff559c97922a3d112ff0ef4af9d6583bec1f84d1aa8bbae705b9bcf458f5d93059b90fc2217ab27d0072a38aec3229d13266beb3015ac2389a06dec3120c6c04e540886091597919da293a4a8c0812d6cd336d5c5faeb64162ec0459e252d219bed78c4b6bb61c1213939bb3cca12a625ce5a45001d7408f6d40fa9466377caa43afe961b5c1602679220258fae72a8de2ac69c0dc97c90c270e306dbd8eb681ba9c092896b19a8d42665b94ff4d5b8b188f19f7c44abc8f88d4ad7b5df1cce3465de377072c70dd20dbd6779336f05ce328ad741d1e4606dce7065347df111c7d3282c8a3fa4a9458561c04d1056cd53ec5a8ddd6bd4434ac910c69cea0443fd09ee32d1256da44ab7896867a0c97fe4faa4a53b6db5cbfe3812a6667f04cd318f3da127a0dd46170cfbaadfcca863e0d4240ebec1cb2a5952881fe89804892d36dc5bd6484cc78db41bed868ed1b321a680a293bc29c420cffb5305d15fba05c76c2138b986f799b6a3d061658e498204c2b641f2f2ba73d633538eef6b5a01117951eedb7611742c120ff24261bea605e94d21e452ddb9ad27af08ed972b7d5e1eae010ec5d83e4505f6a2b7d9a0bb32a1fbba32a2a8c7823e736a69f516b781fb5354be4b0a67343c009a09b8f656c34ab895f9213531fdeee911d677d1cbc5e72c0fd1ad1f3b4b8bc735e14c3f75f1828ea28c90cda40e0cbdc40dec37031ff3d50305d5a8bba1d53d2f176895e53faa3067129a5c97505799967e55e4e9d87faf5920d71055009fd060ad06691b78583f63881b566d4a06b639c55796b23531ea79c6de24092c0e6fb4d3dc739f6d82ee3ee39f229de4c844aba36432d6119be0d2f02e5f72ef1d95fb2494522a7221e18e92cf22e00010ffd93b89fe60b6895a37fca91aa2fefa8debdae3147fe4f01a6adbfa0a59a5203516b2cc7de5faf821a2e72d43beafa30ac379791ad1e5da3286abecfc7a546b80191b7b892cdd01c25e95506471f5eb74568257439aea03300e80699909cc06db2fd607f3279651f7392f80bf4fc61d66f0dfed7b7db09744139d7374d3cdd18d153dede2a65f26130506acc51d5c721a7989485a145dac9565ef6d3cc938c5a51f31ccc88bb0739920ef8f0a01145f4ddccc74790a22a3099a4b57e31b3a01b4118c9e6c393c1304cc51ca1784db5633eb96ccdc88f8b732815b92c9072dbeb61a2cc1e6b2e7098d883e6174f5af7bd4f129389250926e041ba94d1ac543aab6525f151294060791fd26b668d09302c3482c78e5f3271c0150c437b4e78b1cff6f2b8660dc310965f2df14a1f2ad45cd2759433c4f3952402fefd79fff00dd309c3f09a58600223441c11693cdeeaf0a6100d38d612a759a8e01f753982803af30c7470f7bfd1ccf2c08aa0b187382d25868a9fdf729da10bb0aa0e1cd9c6e695eb2c80c6b6ce62737c3e655246edbce5b8f7ae21c473762db0969dc216a93d4db239f67dea74a1de21d50336793d1ae45e931d975bc706ea718a2ab10d66a59d9d23f76969d870ac279611246ed3aab0f79e11611b312624d78b88a9d1a49dc68d6968f7428c33f0a7a65675826422f7ac058101d2f85663de331345b3a25cf76b7c8fe0988a13278be9599b8e4708526b44a70bc31ac5c278ab739e3e6f0927b72507f34b0034e7fdf43364c466bb75b559e03d4d18c864714eb6061f83a6331b3f59dd62f39bfc2529d5cc68bb6ce63db1075105cbd7d7c4d4ab68c9e65a32092e34e76c3178382a965f49386bd4aae307128242a2ffe3022fd7dc1a824b330b9f032d55573c2f004a6905178a2479ba8a2d5b3140ed5f3e10d986265d8b4cf262295658f301b4d36281611d9c61624928da9abc51ff9a6eb481310511772fcb1c1786203d25295e4a319b9c6d65ccc966b4c5795e6e30b2b3ae8246c38b4a911d1904145de63dbd4470fac47f8ee3eeb3f58b5e665c26a316362382ccc6bf8db7699fa3334cb2ce61c746a7d3af24d8030df6759835f5890b7dd1de538cac1dfe843ad06eba2e887f08d9a49b39246fb26eff5cacc937d63c8d0136f7a8ed2af4cf473f3f0d9064f97fb4fe9938d631f7cea3c617c38771553eddd606ab80bf792f34b44111933796fe1fb8bb104223a4de9e16e17321ea7f8de3306e75a2bc79aa5e9c0ec8dde9b3dd1f2ae42a6a278410afa8fb62c16282f1e3dc1e2f8c28d4538a75b5da7645101253dd43aaa150b273f73e505d490490314606264c737bb344b616a80a4931825043a740ea4f75847e98cc99c6880d3085787903e54c63e90b60f03192234ab20cb41c70c6e82b00e0575a1bb0b0f435831c9ceb9dacd1fab8a7328eb3e28533d5bfbeace430e21758cac204631bf033752f947f78ac2bbd9423c2baf4dea22fcc65c96c332ece9abb20fed504643e82f3ba0fff213635910789a2fe1f2cedef68799fcf4a86d63ab0ccd395d6d4f393f7ee8905eb77df32d97592fb34ac86dcf20cbe5afbf9e9cff37bc34d75af046a09a1781cbf51ee2e0b0f40096d85413a30de974c4d1d16ec06c0fad00716c4e10f8dae46ef3cf27ccde74502b657d3dd26b5481d9787f5c6034083ff88807896da55fd2c951a28f15c8c9e6c86ab50c369e5ba4f6311de505c07c7b85573b5a539785820c672557cee4b58dcda948fb51c95674c23f1275b423ee5bf3a646df19bb5dfa22747857fb5c605669f334d116710bd9f1495e242bf47d6b607c1c9d9c706ee770808484ba552c978ef64daabb642a7caddf5a55facba474b8a63577ac817dc57e48ab072bc6a2cc5f5ae96edc45af41c896cecd8acfc36604db3b7fed9d2d17d429f94bd2542b194a3d3405f46c1021ecf6bb907fdfb4b53fe445d5adb18501aa772c9ba75619214384260306ab68a5ab59161b\nCiphertext = 66c03198b3422cf3fd8291080f6fb3ebd9ad863e41cdff169becde726946a342ffa0ee547a27bae28cc782d95a90b0a618f717e3beb577354bd91e00a7a57485588265ad2dd0ab946926fea7c754c42751ec7247ee84c17262c0ed092186ec57d6044f0ac9deb21da6714ec7452e441e687e138ff144ea95636286263685419afd35f002830765d810b6f60e8dee0e6879995e9272c798b067d5f99f49e460b86d67c641f48240b61a16dc7cc27b048e8b8e8e80016470ecd2fc4225e29bb127ab48dfe7e7d5a65542176dd7ad40c07ac8b92891d595bbd7afb63fb6f9e1c2aa2fc659aa101f9b6a5c346625acec86fccf17f0d45809f3b9ee81572e5627f1afeed4ba96c6d3ed7e9232358dec01a1231ae7b94ad4675239f3b456adccec439b3cdd45504c5475bbc77dfd242e5e9671d103ba71a4601a7322e0e295357f335fa8d5651d528dda66575d106308338993e615b1c5bd7e95bf3f755ff726b4ac6dd5a43ef061ac9783f8f2804c68f66486f5844969103a36278ee0d10798bf8a802d3fee3a31294bf00ee74f087749ab3325c027d42b55b197469a5312bdc5c9b316b20093154e66605941d58f4db8d46a815c06f209c1dce2363771b5a794dd8d17e93a2fa7b194c6a0b79793c06f002638e5e3052365221232cc4b30adf161cc6e7865cf02911e2ac9b0a75f000e7ef3aa4f3c7438433513da7246d421f208b179763651f18e22a793961e5976a74744696912f22915244fcfbefdc472baee0be1e591d6503f2d9511ee1eededd9f5547c95eb94de134d0c2186109935207a23b2b8420a5858d831ed78202be855cc6b98d6663c1c52e1a0022ed7ebe0eea6b107da4cf50c1c7fced9744a914a66d4604a081587ce4b7e0f96ed408b8a9a2964314b1334a123d5184889958e6467a6d16e7615e5364e09aab75994e2758345511113321a3436db79351c63a282095ec6b99b6d775a5c09ea3f3225716e39e14df260bdefb2ecfe9a65c73ab4b3712ec842e43ccdfb535e3685fa39b4912719e67bbe195e5f0fe6c3aaada2d81b669c4565921f6c183d708b50c3f7172ba841815e9351fe5fbfe2fb1fabeb7cec9bd1dcf2d6332372f1b972b5144aa7ed6c5a985132f9a54469097e2e981b9e75a7df48fa79d0736c6f8a201c7c7d0ac8ac6512a7089514bf58442dbae0529135a7f2455e0ee5716c6610bd7600b3159197bcb20ca055695a36597bf7d3b18ecd08031b4ce3a643951e231c7ad15481e32ed7a3edd2b379c8e96d3288d5b93b562972a04f1b7e0abcc5090cb8655422cf5e9dac0b49678138faec81c78f113255eaa6110e95406a7e7417a6e221a8ec7fb9d55643bd589ace2da70fcb41722e66e0efce932cd7a34218375b6dfa3df1747953b24a41f94e50b84bad4d130d5dab4194665338e06f102f46badc5dad7aa06edb01f8a31244dceebe5e2006d6ab4a31582ff46731b19071c08ad1db79ba018687f3e6afbe703b1de26c11bc8b62fd6b2fa3219fa7190379504820abc97ff6c034f7850e2c7fd335462725db6748fe45920c213c539356b691f22eb490faca24e99f0a044a9f727d0786566ad00635983692ef324bbf1f80c42b269e9d5a8df3249873c51521c81400c729ed7a5e73995928abe94d189cddf2774f1735bc2060bb2240e558699c365dee45fa68801e6a1745e03736ced1b89fc2755565e3b36c2102594d43c451122d94f4a263664bd26b2fb5bc7700319f6b08796864f92d0fdb41710910bbc13aa9cc7baac3b48a24e4f3573f315448c317c149ddb433d9ddd2a2f0cfc81c22d3dab31f184975355b41e4b36fd8f22e8efa01d61a5cbb0e4fcdd273cdf68ac73fee745faff44d44d93c5a111aefe4a5ca8e8e7c075ffdb738cc5b6466dff78ddd837c72c54941707b04d60bc126a3a2fae9540ec2e4672ae13de0d927a7bd363f8abb5a56364d6d564df90a46df9fd59e2c54d5bcb8280415257a6976d8fb24c33330af32600cd1559e0eb05d55b34be456d434bc",
-    "a98252fa531486ce2a24c8bdea1d57d93a550ec586920903a39ca61cbfbce79b8f3a5b1653794872b2c614458177e748f8dfd43840e5bb0d608c26389347673fd0b005f60f52c56731ee5faec6c8d0617fb53d5f2415c2e7906ea0e6d0066354b213b3e94f4dfc311e4ec6afa7e8d1c69a63cccf8326741456a5e0bd0a359b7a37c117f7892969ad7b70cba9bea0a975ada7cf67e0d7255be8d2c6e7b8788b9ff14c5d1449d6173e07b5f9d94560d46f474ab2a67056fe9f4a9fd617a617d23143adb4e7ea35f2d5cc1398fb9ed43ddcd10f28debb27eb13533110005e6c78ca4a874db68c65081ecb8bff1b64eb1e2d7b76a1da3b375dce8a92d32a6277ed847879345717b9649f27e846a701549311c7e69a96d61df616157a114bdf1663ad93a26c28e1a62ee4a7c72bccb9785639eaf1e569decf777bb0548ad9ee36788cfa1150eee3ca3c96f09052ba2300cfb7526b9424b6f7418c27a1e9bc13e4d9868e5c330c051c3885e44714bddf7cb090fbd0f36b826aacbe191dc8c35c219e19fe736198c29dc4fa1a98b5fb1805dc29ecd02f74d4510a3928448b5ee61b5991e46644850a4885bb1ee272883faf27962430de1922d0883e7e80215cf5fe7e8f3fd0e2a49bd50727af793cb7e5b40860e80a1fbb9d5b5696bdf2f741909ab5a713de47716332df6c4f78288edcd6ea130d895fdb2f29f94635bbf2061de55f1801bd6a24294aa199d78021a1ba771c651de4bc08f032fe6ad7a5caf6a6afc6de649b901f783a0ee0fea9b803beeb0f431400d0707f159d7dc29c0c334a918fa08a653137a4a8bc86066c8800e1d171f1dbddf1fab8a3eff6b5023da96f002e7e217e826fa378b15dc8a376db30228f5d6b629f331a162d63e53e5b5bd7ff9ec098b4314285908281930ff0a8aa86a6d89411e6b5bc6b9c9e931623ccca6741fd6d36311e6a8e323a37ad40b7a2797b84694e736d9c135e52d149c760e727598726378cd674b0f4df1c361de0a12a2b8232e611d789bfbea699e8e77b99f3449609caff3d6ef7233df8cfc624376c905eea46c6f77c0b01d288868a19db77e227dbb5bfea5cc3f49d219c7477f7f2b3447b0b8efe08eab8f69579d727555e547c13ec7ae13b83386f2adf634140c311b6e2759cfb9c8aca1c32bb7c002d0f46ecc526916589a29e328ded9679c2163838f071b5b85b35e5e7d99c3c45d25bb9d37d7bafb8350ad4695a6e0cb7ea7d93868c30bb54e301e21147696b7dda156226a5ef8c62121e6b2cad0c4e192116192012468eaad46bea69a140aa3cb9056dec87c911636a1e55695b9e5a27c63cd8c03f31570d4b7507d13731ea31f082b33c6db8dd6e22282f9790be41350a96abfc4dc3de78e0a698930f540dbda3fee923a463a4c4a66bf00bb2cdd6d22b62a47af96b78b1f0f0a174e4ec5b785b3820f47d3c8cc1691d4751ce4e4ab78a4551956158a36717dc35488e890d0631241906db565603205e054815aaaaf17945c3372dfc7193369871e2e88fb84c15a2b9071101e1208177fc18397e6af17b5843e1fa75392d8d3ed214975d50f2b19c24e83f010f8c394ec1edbb1cb912e61627d2760b0e630b986bba2ae113b8f3b51ba00ddc495520274a85e6f6fa7573ac4ec6e2a86a1da9199ceb007aa6f132e5ab8ab8fdca7c829f452ff17524fec475b8f485b29fc6f0d972eea4ce98e242b5d58f6ddc1b3a71256de1c584c9914a3cf1e469f0033165d934fae68a7559011dac7a4e0c72e3b398fab8f8cc2fb67963b0f9220f410e5ba13026a27288a1d49edfaa51e8f220503fb5ec476147cbea975994fffde3ddc51bb189c470078978d238f5287fb2629d23989875d74b006a4122f6a342c996d4a244e8c5e4b804a44c301ac4d6054181a07964b279e0a44c158364395a2ead40053d2f3350ea0529a57552ed835513f533ee0c4b94ef674f31851616a4fa2d0302d13cd4aabf5f96ce28219c0b5bc0e5410fe0fa387ba1009a6f2280f9e7bbe20c33be5eb411a5f6327714b3443b4152cbc54c4012473237dd98b0490fc4228ded74afc81be2a58a22e03ca987faef5310e474f4f5a183f6b7ebede5a8df8a0f94a87a41852826b29466fd761f40b416ad0f263dd34e5497867766a361af1654c3fcd6ee7e6bb3f72d64cc980f04305b63bd574f116d1aa35b4bd642cab0cde6a29139aaa163805c6c40384313d4ec6027c891023083988c1b0d2edbdd9b1afe102fbda285a6f897efff72a0d7fc19a3cb6756cfaa2371e13be3cd167cddb90d525cba7da69608b9995cef92a6424a14df6b860ef0f09830fd7189497a432347680de0f463c0aff82df8098cc4f7753f7680c8c7374d01046b05c63be73f3a1623be778fdb0bdb90d4fb4b458af2890d15f108b0927304c91c8d62cb148c35cc93797db3ef9bba1014d89859a91da0c0a971f330600d71565d30e9c9ea8c07e7f629e1a6d578da04d37e597261cae8ab7d9a952bbf71573f1bf70e064f36c032cc624e3c980e5ea46d36232d61a57fa598347b7fb6b28401e34628b051d6ca3dea190d1d3c343fcc83175f70f77a8fc5e8791b9788989df1e37cc4881648f4fc673772003079adae55c83cf02a894b98561e4a6e4416bea3df18d6f702ad5c4f40faedec6b53cfdb5b3a52d7d43b97ee23ccfa2d30c7264ec555b15f1d9e7e19cd9890a7e8e01ff21d3b8b451e50932f189a420d18e7c7e2f103332c78c84600e5e8fdedd84f055a8b39be9a52782d47c6205c0de41644b09c0931f2da269a7e58e669f3b61ebda28ab8e3f9b83ff3d2bce37864af494860b2f01b000abeb737fbeaf8f9fa6378366606dcd0fc33031b94f9a7a0e562c08ea720a671ff92520047f69b138b4e032c3828874ec4c29e49aab302089956566372b20c0216b601c3958ed9691bbd89f1df45c6613d469e3b9758a70c860fddf768b10a6bf70237a454a2c0b70dd5d02da612a91fc5731513012a4a6fbc16d01550bdfdccaeca22bba104ccf6aeb19f21d4cdd3da231af8ec5bf2a726ee9cc7c85b8ed46d2f6fa4f1b010b2561fb69690d5a9df76d729450a6e139962bdaa2bec0254c5a252b97e7ce7eab1817f454c6121130952b8c40628065dc9b77b0f953552f5aa3ff983b6a51a51dd87c2b51a18e14adb8c80e002d0b47c61cb357babbbe3ed51d371941a8f111837ecf0e45020cb941de170c4a1b5e61bb928b1b11a8d902febd2ba016771f171b8a7ae825fcc4642d95649d53675d0027822e4ff79ffd302bfab1a0ff26f3648c7ab00c10f8d95f21e40ca2b40691bd4be79bb9ccc0bf760a05be4728bbc0a64e585207d1d09393a80d5f574442d6a933966777ab05f699c4e84aabbf753059287e7261d972745906a4fd8967bfc80ae9b6ec2ee1b22a81775f4f24999987365ae2dfb6739902ed51b9a4394fdf29f216c34567102d9db301661b09b728a79e377cf4bdfcf5c83b110a2e267abf6d40947e643ae2ff0c244af168c9f33e7685474ac30611ef95f218e0dd280899a92a41e7a759d03ce3709c2a140ebd35e199f1dbb96f7351cbe1f3de8da8c49758a49b9e724ebd3220ed6f51112944f70c0d1e9178f68a2c9476a913de00abbd1f5bcffa646f926da77a9e9fbdf81cdeaf7f9b13e843afefbca81c93614f8f1675325965b5836b8a77620a5ff162e25366718d8da7781e1a7e01fe2e9e56cf958c6273473abf5c2c8c7fb209307544e1c0726d5571e521621b18b6da3064b473423536b1b76ed75b21b4ee205d7ab5f081bada63062706bd155672dccf84614210d72660095437c6bc2213d9c904a4ba1bfda14d350fa3dce7141e817a50859b1a74aa64560b2ebc67add9f945b6e85577589817078c8ae54a9fc311593d2cbdb6692b089ee6264cebcc7719753f80e30dbe48b64fcfd1037fb9ddab69a5ff9e5898bd8aa947d9ad827c26df67c6786edcacb3478a20bded1ad8c48018ae0d439bb5afad5d39bb8fbaf22d72ffd759c4fa2e94a5a89f41358ebdc4c3aea5110f1965a049fdadff9cf703eabe9628e2680fa4e70320d304ecaed13f513f27220db1916ca1500f1c2e091671fb71329dec0bd6e310c83e67af61b8ab60ee1a8d559a508d174648b1bca451ef0ab0ee2ef74f4fcfaad1cc5ea6cadb8f1bffcb1f2c05122011ebbf6abc16838e452fc47653821589da4cb5bbac10deeea3ba0e0a6241338e64cc78d7a923d018e8b5b51c4442070e5b0e6f1e8c2b83791e930899c5897a602c401c1b85827962ff56d19c06f5af033059bc7fb1bd29b65f66aa5b4397834e846935e523b16438a42c1f990ebe4f83182163ca5fc60a4c6d77fc182e81fcda943a962e9e7f00f6399728b48bbe38d8178fae3582c8d9998e49df5f28e32d541636df3cdc8ac00df45db12da2e5e76f366c1ea8667ba5f3542d21f58ead7c55d06a4b35251b8f77dd34d3de262947379107a06d2f4891ffa0ad3a3e5bb2bbbb978af4953310d4cbe5525ab344ebb98ed24d003600de8f3af36ff3d0a7efeada963845d573685bec2221403b994f97b1e714fd7dccc300b62c2a516e9c6780983062eddde0178e93fcbb2ed4f06f60767356a11d22ca37078fda1ddb3cb907d1020f62ba85d09044574ba28aa3df36988eb8a41e4305e5b0687abe43a90e4f68f0374b6b05049aff5b065d7688cbbfb0e96ab03df38903bfa1c269f43a114085eb4596aec87ced88701b42f0b7426389727308bf10aee9d8f15ebdc411ce1e764a290a12faa2d7c1126dc7b5076f219b826ac8d380b69af7f95d69fc3929a97f5c7da1db6270e9ee1f2a5f7fa3a1b6bfcca00463655121f681d3a627d03efdf0b5fd045fb153bc4488a9a8b7264373c710ebfdb1c267fdca37723b21d5c3eaef48e784bd76e27c133cbc24d114f610c79f2a1f2c30d87ddba395887030b65097ca5566eb0361e70615b46d4b86c2759f1cc2efa3915b4cebdf51a745fb3c6cec69a1fda2ec5e884dce228e30af362815d2d8b59a14f89606bc77439042109369a9648db7d71024ed6df06c8ebd22e8623f48feea77f48b5e88827fafa84b0564151a5997b7f29c4d3d18068e34f2690a293d54003d0ea8f3bab9387ca72212cedb5f4602ad047dbffae2ab3a4cd2865bf896cd96f78b90e4017eb7e3c7092320c0a37f81dd65a5c4817a4e7053e6d2bcb23b11e09f681587f3a9361e974ad54b88c72c296629b1ab754d25be15e87c414cff975fafb3d7cb68167b21f1889685a48966705222b525fa47143b00041df94817c275d93c2550fdd82471cb3cc1b5644338060b767e807bca902c180b3e535c77be2651b3962287b6d1f6403033de4e0aa3a20615ab59d290f4b167325959c1524ef216dda2ffce86b50cb6b56b62a20a043d9d78c704479c22340151df5a1907670f8d4f8c90d93f7b5d94d04a4d383914867aa3c0e5ac85fc299a4d2801a3f80f4b0f046fb62c1c8c539a83b21c7549df0afe200537b52c80ebdbad8a438e430cf876cbbfee9ceb1bc5270577c27d53b40ac153cab377a565b1a9fbdee8bf8e94839c0fc04f7f664383bc90d56ccd1cc01b465c250b158b5e6f321c20db245602d10aab80c553d52f17282b095b5e2234c6c689a84b096112100359816cef7e92029fdfc048058f847cd2f2369ceec9fd171a0487bd7acfed6b0319832df6d59affbfd460ce8d12e4171da0f094e872a2888fe74925c5ef0621c4edad337f7006086748913b24d4d48ce36e662fefbe672b6d476456b1fbac6d80030ab93da93acb4a7e10f95",
-    "5547e7e20a0abcdbf909f05a2ee2e0b7485fa16be652b9d9fbfbf01f082488a81022bdb69af9e6fbe753e9eb92a1762afbb4df49f83ffc0cf03db563aa96fc5ba1af6d4d7eede6067749e8ecec79b63e09742e29e99e1c960dfb0688b0222c49ed919379ac66e3fa1c72645122d1664721e78fefdd1224c0b886f6e214e37d268ca9acab76ab3adc9f5549e5dcdbb3d31ac34ac472894d004eed71f88ca2377fcfa48d3ae43805dc612891dadd06c263ed8617194f890bcbb964f010d277ddce1f6682e661577ecd51a4d5421f00935a5b24fef0ea1809fa5c4fe9cf8c453046f61136ec8872915d2462157d73a205d56d77bb83cf16b88cadf6430c0e5397fae1f91a6a11b177bf04b065a2e55df81d5c086ec8dc8a0a660eed37d41fe4d8b3e3f22238e2a63b6e4feee1fe9a140ed37b2be4193f75c2d038aac7f6b7dad2a3b37e5b9b660615ec1db77a9b7ab416f43e66c872b71cb67c9245c757dc87723ab3b9544fdd8a16c9486e8ec3c4a44cefd98535d6e5683426c1cc8c888b8e0c2e7528bd7eb89b80d9e00969efd2f0a0fb09845426edf0d1d9a0809648e7e46ea0a8c9988bf9df475be12a72c7326c1f2bf01afafb190cf6f649133c7dc14ecf9b8c971135bd303c8894bac637e08257d45e1b68edf550d896c41682c002396e8f1eb7c1e2f4e0ed9b8b7010fc7847e6fb1c5907c17b2d2b7cd24c96f47406bd04cfcb2099d82dc2902d6f91e2f8f3a05bc62019af536309e7847fc06c10dbf7272a1509079fd16bb16a85ae2e078f97f9ce66bba66d6329c7ee70f9688f6d91aa38b25c7f4884658a72ad8cbf96d7d7a9652673273ee1b3d4d17780dfe9ca865416e318bdcbe9efd8e071fcb15ceb0743df5af4f7d598b31e38677e65af61c1109fdbb11fb11e3952e6c3ae8abc3f894ccdf205ae55dafce1dd05dca6b899877f57d712223dde4e7fdec7e0ed4f0a29ad359e318eb36ddb42fb205adca400f5b2615947c4f0ede95788093a1152d88acbbbb272750823151e245354e658452a95f21fef05bbfd98a10c1c975ad1a08c59fa3efa9fc73588407a83d0b26a53f1b4115f83780bc70ee2619d7374ca45b9e200055df1b93977e17aca89a009110a6e74caec7f86114f91975bc6e8bcdc7267ed2920cf12cd7137840628e1b8a0ea181dfef18dc5f74e752f842ea91bdce4b420ee709bca72c4514e92bcff55902e5529d77fd95f5837c8f4fffce80c813630550a0dde24092a25f65eba90790a06f4d4c3e739aaa8194a147fb32e81c71d3e8def79251c33637661b0a621a2a6b302dea00d34a9dbe9b621c1dabd0464e85241aa6712d90b4287cb23c17bf1e4d0e6dbed372e6b49c4a843305b3b0e5cab0b0964a93ad0bbc99ee711afa7f2d0a296a375fdb3176c65a957ddd9b88e9d57df736acdeb02a71b924cc2e972f51ba68a597215678573bede9ca5b3a0a2461b2d3b9ea57a5af8c91d40779bf917ded32f14a66d96e28e1415fea1e9306654c6b84d8a64243a5271c1f11590423c718961aecf5f659b49f67efa78e02ef2524d0966ebcc446d73d49ab7ec31f0c009069d14ccd63f926169291b83a3e37610054b0b964741e2ed8771d20bfa225eac0280b4d5af0c09d3218bd497a035536f5af0816884d606f1a872b8161a266466b56e0be8b80a7bde65ac706eea8cacf1749e5e71ff9fa3e69ce878427a0728d44e666eff977026abfe18cf3ad156a943b917e72ad65725a9a8d60b7b5740494fa63143a7f2a94fe6d8b319be55d6fe1a988244deb798f345f30dcafdb6af9e9cee9e35733274bdf3896750897371563ed2516c4ca6c3c3c994b48cc94b67e8129d234a0e19dabe39e500214c0ed5f0e5d61b2f58d7355d147102d93b2689bc5185dd4c0a18efd11a307b887d4d0fa84fd992731b3a80dbd027dd36cd6933766c537e8e9e27d35d5187e8276b0f59fbe7b6d629d3416b782e7981d85e1e890853c3aa94a93c1667a55044ae42badefab979fe7d525c6a180307c5ee3a9c3933038028c3e1d15d1e78fbf53b6ea61ac5e02db0161719398a31570c55f73cb47ddec8f99e3e14af5adb8d5cd179f4204d080331e75bd391b19d38eb81f148c36af3e8a3ebe76209bb75c9741a89b5d0708bb0fbb0945fc6fcd6ce142d19faf0947c338dbc8d976963281866b5216421c00cbd77c0907d1e16f5e925319cf6c62f8c6e8eff0c2f831c504e7a1c0df09a54e2af708ceef39ed7d0f63d83429e9b0920c03cf85c2244f2fbac3958847113bed577dbde8992cd91be5833c75faedd5e2005d4f7b66fab8fa9305927406f863d1795dfe04028940b765bd79de6972dc7094fe1c2503a73d7b50208835216c23aab3e47094587549fdd74bb50ae21cd1354daab632fd0907e63f4c2b2d39d7fdc4fc216bfa742b4608238623cb7fa01bd851c1e7ad5ef5215173a71f363fbb7dae8092486f4a1549e32ae53b14c1343ff7fb5e2b1487d9c594a1b56e22625d275e41535534d225b7b2c9deeb0d30dba7188cf75d680d4545ed05044a0661c690a37fa14a73ba8c68357e2c948e290b5d9a4b51822824614ef2938d19ea4b650041f59f3b548f0a305b86f55e69760f37f09dfdad62651aa5fd84eef28a4431136b34a49c9bf1f2891364f86b0aae70b0414e821e3db1533b0f1db5fd232308bf118f858aab5ae974c10583f61b283a3870eb82aaa8ea3c4e2ee3c3a3d7169aa8e975ddee7f620f6c5bcf3eaaef0101b62cd54495cb8809052c9e3151690cff7c1efcc4f63b22472111a7c5d9d7d2a2be951510f60dec8c426f14700c8630f8a14dfd359addf5d9b7ae031a745ecb4e17321b385799c90f924c4780287ac187530a40b064064b9036cc46e3f87c4d23aeeed1bc22a5411c7c503594d5d1261eb9fc4da242493beee9f671485a978a32e965faf9b0e2c13f78e31e1630b72d35b4be691e90b3798e18223c1b514b39a8e1eeb7897c22fdee1e33fc76e2b2f9298ad4fd89f44163aaab23d754d98c7890e58708b81b3832aee31aeca85e76416133710aeba0e5d9f17695e607d09ae3f94be191553bc39c6df03cefb4ee05516fc02d66c9866e4eb0d89a662e309379a347159db2e070abceee226f2b8b62847ef7c51d69c5f12eb567fa13af4b4f90b3f3d9d4b6a3f68bc4dd77075081e2e99833c18b154d0d6ac360141de2a25af61d551f10a34e03e1419a37409b4c177c51a8d248157b411868eb607c34d2daaa453a0954fade5eac45d5f21f50efba8bbc9c87ff0435c70f064b42cb2d158384fe0a4d9c90030ace7723af0a6c8faecd8f97f9850e2a489a94ebcc655301e2e14711de9eb08726638a9ddb57160c5545c152a26860a17dd18172bfac138a300f60431fc49eff18c93f71400e887f878f4dd637cf5df8c1e2b12c0f87e31ba2754ac1748479eda0c4184b528554106128320dcce349939e5e6cd3434f86dc7adfee28c008a21ddf9d0dbc87ceb14cc3afbef1e06fb3f9908a4b14f5e6c43b23ba783b75a6cbfa2ebac6533661b8c1143a34e8e2a9723389c4b7087dc07701c53b169894551084aedbb423bcce2f470881fdc7240c26b3b76fd6cfeebf8eb2828b4741e5e8698b19fa0a44703cb4e4c8ed6a7e4d6063f5fab724e08a159f4f04a2f351dcfb6335ae6697dbeca25c76b55e6ec9045eaaa8706902df492b8c8cfbf68c4cc1be5d1e5a173262e38bde051656ea85ffe35d97f1b25f6a47381bc327a946f7cbf6210adfd957b2921\nAAD = 85ddde4720659e80e25168585a354eb1e021c0b5d2ee289f2314dd5aae52bdf1fd44755bb56a6e659111a1d4b4da73315bde01c7d2c15a4f7114aefd68c141049fac27acfdca24e65c51fb1c27d307cd948e13af2963166bbc9411401d124f1ddf20f890db5611385257f52aa05c09b467e3ae886decf5744ec3749e5879f2a60017f601bbee11a66604d5f3d521d2c48cea1794f77366f29c7bd12a8aa51d34a4f3fb52809561b527016bc6badf9d136156c330e1d69d1aab98c7caa9cb46e782a898b4c66e4ee3e2445fbfacaadf9a8f73c4cbcb2a1ceb604ba5637b51337fcbe0fc366da98e805ceeb29feaf05420113b16e1005079c0e88af33f5970b3d7a8b51d0d9f5120a0795063db508171b75ed07705ac6d6bfe4ecc59243091d48865536515e036860affa880bfc91aae2fd1700de15994792aefc4a176e5d49d0f9135c7d670f3cb8798bfbe83fe73de7427e0f3e6a2df561cfa15ffe6ae80d5016096c8875b0beac8cee8fb530fb421b9a8ada4d551a528d0a0b521086f5a2db371a3bf12a2ef861f831fcb44cb2baede907a9306d3e5a3af796e0a50ba2c8dd61fb03727df5f0654d837dabee2fd90eecb7b2e8f303b0d57f97dc6a52d8281574d8457c89c6a9f5d80e0bd86c90ed39b1db4253affee614e8cf1ff05166c66e7d2a2aa2fe8a81c4741339683debe189c126e7f553a5f2dc16fc16672f74aebf94c7e3041c758fbc6d0c7f71c192cfd0fb2ec52d0a0705b05815d567f3d19f9b5d553a2adce9a79159b0e38980851bf64e97f896c028a6df8363cf1f13f4654265a7b0c0b24198efcf4418c32772bafd3980dbc689fab12e85b3ef4a491e2e5ffaa2fadaaf3deb392105a42380797d3b41ef61303a6016b269ec9a9f6e3f26070ff33cb467435ecb325dc7e18728a5c2e882e720c8f876fef10f5bffd5a925cdc9689d934272019e90e3a3bbf63a295f207faa5c014e1517c7d5c18c3ed70e92304d51944dcd3604c999d4aa8d8dbf2a4c69cbbc08635c968a20dcb80f438d43c57851c4cafec0b9568dd6c19932fd3f1294afd16f019f20e40ec87f6f5dffc7717470614b2de6e9000969e6b7e561cf91c06dd379a09c6c25c7841330dc78fc5be1d9b86581a81f55c0289531128638441fc98a1ad9472d74e2be2f874aff2fcf9c941502f59f716185a4c39289ca368c6dbf5257b5dc5e57a420792c26e602e4ecbc4f17c8787004eb88ea091d6b6ddc3c85dc110b5d1f46f6e1d872723176f4c73664ecb4219258fedce19ae22360354fa4894fe51d69434c2e58e1ec665b5cc33bb295053c591b474b6ae178c8834667bef971604279440170ebf3e739a4ff19704e5886767f81edce95a3dd93d1147995e7eb6c794b7be136658ed23cec7c374705ec0d8479dfb44cc7213076668e5fbe6a508537a9157815c6e5187b89f\nTag = 469e3ef168a64945f76d7a2013f27b68\n\nCipher = AES-256-GCM\nKey = 0000000000000000000000000000000000000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext =\nCiphertext =\nAAD =\nTag = 530f8afbc74536b9a963b4f1c4cb738b\n\nCipher = AES-256-GCM\nKey = 0000000000000000000000000000000000000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 00000000000000000000000000000000\nCiphertext = cea7403d4d606b6e074ec5d3baf39d18\nAAD =\nTag = d0d1c8a799996bf0265b98b5d48ab919\n\nCipher = AES-256-GCM\nKey = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbaddecaf888\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255\nCiphertext = 522dc1f099567d07f47f37a32a84427d643a8cdcbfe5",
-    "c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad\nAAD =\nTag = b094dac5d93471bdec1a502270e3cc6c\n\nCipher = AES-256-GCM\nKey = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbaddecaf888\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 76fc6ece0f4e1768cddf8853bb2d551b\n\nCipher = AES-256-GCM\nKey = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbad\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 3a337dbf46a792c45e454913fe2ea8f2\n\nCipher = AES-256-GCM\nKey = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308\nIV = 9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = a44a8266ee1c8eb0c8b5d4cf5ae9f19a\n\n# local add-ons, primarily streaming ghash tests\n# 128 bytes aad\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext =\nCiphertext =\nAAD = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad\nTag = 5fea793a2d6f974d37e68e0cb8ff9492\n\n# 48 bytes plaintext\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0\nAAD =\nTag = 9dd0a376b08e40eb00c35f29f9ea61a4\n\n# 80 bytes plaintext\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0c94da219118e297d7b7ebcbcc9c388f28ade7d85a8ee35616f7124a9d5270291\nAAD =\nTag = 98885a3a22bd4742fe7b72172193b163\n\n# 128 bytes plaintext\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0c94da219118e297d7b7ebcbcc9c388f28ade7d85a8ee35616f7124a9d527029195b84d1b96c690ff2f2de30bf2ec89e00253786e126504f0dab90c48a30321de3345e6b0461e7c9e6c6b7afedde83f40\nAAD =\nTag = cac45f60e31efd3b5a43b98a22ce1aa1\n\n# 192 bytes plaintext, iv is chosen so that initial counter LSB is 0xFF\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nPlaintext = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 56b3373ca9ef6e4a2b64fe1e9a17b61425f10d47a75a5fce13efc6bc784af24f4141bdd48cf7c770887afd573cca5418a9aeffcd7c5ceddfc6a78397b9a85b499da558257267caab2ad0b23ca476a53cb17fb41c4b8b475cb4f3f7165094c229c9e8c4dc0a2a5ff1903e501511221376a1cdb8364c5061a20cae74bc4acd76ceb0abc9fd3217ef9f8c90be402ddf6d8697f4f880dff15bfb7a6b28241ec8fe183c2d59e3f9dfff653c7126f0acb9e64211f42bae12af462b1070bef1ab5e3606\nAAD =\nTag = 566f8ef683078bfdeeffa869d751a017\n\n# 288 bytes plaintext, iv is chosen so that initial counter LSB is 0xFF\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nPlaintext = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 56b3373ca9ef6e4a2b64fe1e9a17b61425f10d47a75a5fce13efc6bc784af24f4141bdd48cf7c770887afd573cca5418a9aeffcd7c5ceddfc6a78397b9a85b499da558257267caab2ad0b23ca476a53cb17fb41c4b8b475cb4f3f7165094c229c9e8c4dc0a2a5ff1903e501511221376a1cdb8364c5061a20cae74bc4acd76ceb0abc9fd3217ef9f8c90be402ddf6d8697f4f880dff15bfb7a6b28241ec8fe183c2d59e3f9dfff653c7126f0acb9e64211f42bae12af462b1070bef1ab5e3606872ca10dee15b3249b1a1b958f23134c4bccb7d03200bce420a2f8eb66dcf3644d1423c1b5699003c13ecef4bf38a3b60eedc34033bac1902783dc6d89e2e774188a439c7ebcc0672dbda4ddcfb2794613b0be41315ef778708a70ee7d75165c\nAAD =\nTag = 8b307f6b33286d0ab026a9ed3fe1e85f\n\n# 80 bytes plaintext, submitted by Intel\nCipher = AES-128-GCM\nKey = 843ffcf5d2b72694d19ed01d01249412\nIV = dbcca32ebf9b804617c3aa9e\nPlaintext = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f\nCiphertext = 6268c6fa2a80b2d137467f092f657ac04d89be2beaa623d61b5a868c8f03ff95d3dcee23ad2f1ab3a6c80eaf4b140eb05de3457f0fbc111a6b43d0763aa422a3013cf1dc37fe417d1fbfc449b75d4cc5\nAAD = 00000000000000000000000000000000101112131415161718191a1b1c1d1e1f\nTag = 3b629ccfbc1119b7319e1dce2cd6fd6d\n\nCipher = AES-128-GCM\nKey = 31323334353637383930313233343536\nIV = 31323334353637383930313233343536\nPlaintext = 48656c6c6f2c20576f726c64\nCiphertext = cec189d0e8419b90fb16d555\nTag = 32893832a8d609224d77c2e56a922282\nAAD =\n\nCipher = AES-256-GCM\nKey = 53a6f0d9b8a81818f2fd7525acd65acbaac82684cda4fd357b1ceb6146b31ee4\nIV = 05d8a92b5a510c3a3dacbbc0\nPlaintext = ac0ae17d3d0ee5935e18675c36d9e43967f6da38dddec14c7ec574ff8473e11ae5019e638232323c175b7672a7462df6709f5014bbe12a1370a1ffb570177927106f995dc8f35bd6e6228de7c16acb71e583c87477dcc7b17a908ce01543496c2cab8a14a21c43b18fab52d8a882dd1d999b4275db34c7f32bcba624d128580d7566a2da4bcfcc4136d58816c437d21e90456fc86381b946b8955f0448e83564165a629cb2edb978e5941010ee9153b054ee429b315058334ad7899aacedbc0bf423de69f57c633b56033c6531dde29258694045c46a797987471ae6af8fee8ad0c1be4149605064aaebafd1c5592e61beca9b5c7771410a276c3ae517490735ddd6af499ff705b9fa68d50650e60c19f5ae2c88dbb6d612afc7be28a5f55556a2163b6f66609f7d9ba7e97c074ea39a618727421fbfbb6453ffeefa643decf11404764515d28fce8ba66b8c85d077c47a54125a38bcb6b0adf6d248ba0a9ea129c887c66ef537c45e9fd3c17ce352e3936cf139e13a5946a7dc9dcb6423ca6a051bf560cfc572ef366940e71c81aa302cb9701f9a5206e9eacfe9835bdacb6425d058022a27fe73e5edeeba98c7a3edb761578ab2ad5a442c2dc1cb3c143c6f18dbe525fedd2a9cee0ada3b2c116465c5cca9a7e5d4374b2",
-    "9aa4ad8adaff8d6b0d1ac3990685240ce022faaa07241f9ff445566b9e0463350792cadcafd5fdf5c37706c0025b3c627185b356d39dcb2244b15566e6e3f8942f730fd6d855daa1456fe294f9156c4b5131e5bde7f2d938ceb6c7f5deb0f847a98b7fd11a3f5d0163eef9bbeb83cfc96dd8eedd447901ff4d3a35c0ea1f691b01385eb39fd265f756bbd77bb61b1741db0502947b4b985382a08a5916da809a8afd3fb1d78d9e16f8e37f51aba100d031d9da8613e9cd2cc621025b47150b3e76775ab23412d74334bcd79746cf601407481310a923047ac68a4e6a7f7b96bcd85bb6f24e38f03c80ad41a0a581b4246ea4715ee561cdc5384a51a6fc9ed8569ba6b12bcd95e6202ecf834dd9062ec539cc8cc4ed64ab9ff85998da0e63161e7391b14de47dfde41523b6c614618bf2fd1edd68a5de1c03c4181569b6c361d955c637abbf4efdb5dbf2f0dd2544329c44b77081a48f53231fa9d4cf6f2186427e469d0cfbcd698f7e7cf773240dd2b807a2fe699f0ceb4a2339e9cde01114b2aa5c3591a82a3a27b308e1e7f092af8ad97bbe7b28d78ecd80c0c0a28372193d66bdbadc0b58e4d5408acace53bd5e12101fcb25754f8c545340fbbd1328287044a29d18f40a24b4084febebe228b67cefd970df6d44ffdc033a50534e5977bda660c589c6e3c3a28b4c500b29ff4a1c3eaefb068784a29914ecab7868a43999833b0b37ae79afe58875a0425262e0ec7e10ee8a6bc1c97d332bc2a6195de239a166486a3c1ad8de3a026e5b1757f9a778a511024a260c9809ae3b22d78f18ac483281a796b1ccbfe7a9b9f357d12d340e20bdf2037e8bb91ef858cdf2eb9d7161a756d8c244c55524f8f5be2e4f18641bc4c2409c14816846c4655be716276d8356e516640da49e8412fcfc7ac0e084a079129b23e54952d8030e1f8ceaafcd322dfb4bd189bb5d940ac83231de0585783387d0642a245183f7a251779bdb12c63e9edbf3d0c94281140598fea9e73e951ce650c984cfb1398f8813abf8f8827af5eb64a65dfd1305bccc45086438ac439a9265790fb225c509ce3c9d39e25d2276d7f3c06d7cc28d33b2c21bb38b50dca5b10afc09da83ba12ee878e0f6054e8d3e78d731671de4f9d5a7b97298b01f37c7e78e5fcf5188554bcf5d42559d3b15153bb3cd5a0d1cc4a96d02ae8b1b115d1ff617b6ad894ce0585f46a2a5f4cc1b83065c1d7b5d2f25f3f4bf9966b4c7d7156931861d5edd199c126f1ee4ff6345023419d0a4c87f3388fcfbecbb6c1e2f4745922b88085d21d4551e4c127eb423db87a51c9f9a140f8a7415dbd70c4b0173e687a40f895404f2203e14ccd61e0e5b0d5aa3fbd1c8affb5807d787d840916ece24c56c50d3d9ed9f19d73f2c80c461b5b3c07dedcdb41402c3826a958d74be48382dc741dcf3e0eb8955397da33941fc47288147736d778914a57effbafccd4cf293e6ed1c7d19b55433bc0363e41546b3638a4c630eb35dc6a074f90185cb9daaa6eab54825a4daf76f49ad918e90e5777a826d6d5d52f32f7ceaf818f87251ab4d1b5406ae94e41cc97fe022b144f26335829d9c81725b3daead621a0df71313d18214ff8dc687a7ab86b8eec3070ee1ca9f62005a0cc15ca6e2f4fd893de8fd91f6210f6c96a576024678535c962a2dab06f56be377dfa74bca089adb7327abd05c3ad7646b5e9e6fc2f29916b34c8642f3c0caedb53b8f30c2a77d1757103b7ed156cdc703911366b02cde87ce7343886987f7a8c028921a7b87c5c0aca7ada34970a6d0d32eb1b177ed8e64c1fc6839b9d08acec19560bf4a815ca6187635f0cabb8bf062e8216d3b09b7abd99e956734129e16a7c4f3beb850fe2b1548729355f9015c9bba336d3e26a27b3d75d75722f7a8170d15ebf77f325c97778a5a9d7c76d3e101eeae354e54e6fa60b58cbdd900751854ede326b58fa5caca073c630f3719d6f52afe675f10d464e8b58e5fde75a4f225063ca48d76efd1b645e4bc89d98215beae765601f635a3bc8cfc08d74722f3d95ccb4cb4e3ea977d0c534a4abb866fb9a31771222cfd998231c30bd16b6844ef71038b67d72c910cca40db7260dff0b74162449a9e2cf15d7dfbfb3a685080e6c83ff4341c95819c0317502ed49af7ae688b52c9866518f74d69b4144500ab9d5a0829b9287d5fb67b78801119ddae7a76e80be8c4dbcec7866ffa7d081406e51cf617be061530b539cca7e1ef9118cc06e8eb2a01425b45947a1d2332e360acd0654bba8f1fa43ece68467690d36f6802a32f03f9ea056e57fd548dd4a3225ad5006c6c931aef1990639498dc88a23895ff1f75520a8009dbde4debc20ecc546e378eb7ed5ea3740d2244036588471d96e4751390b6b76b39816d853944cb5677b493b36de9736ba0fa404ad4b3a7d7c54d0c15072c040064b871401b25b88559d059a9519c7e2446b0d110a4aba9c12555e5f620680d1fea2359bc85cd15b5c0bfd6b3715d647514118cd60483dbc9c83e285192108f4ea6bcbee1f0935044610c68d052ccaba23258d09465d5521e2664d59358621ebbb8f28a4627362c8397f0a9852e5d8daf53a961d4ee66299e2b54d8adf5134ceedef57011f810aea76262422236c3e1a478a759584c7880fb3f32389c4bd4b637caad7b2bd6fd295aefb150754799434e99e0fd45c1cc4698ab14d1f63eeb06e53797cbdddd45e7f87e85b45a3dd0df6335c3b1addee87ba953bde29ed98042d745c1465a967ef922993798966e1c8b96ad6f68404136be0caa2264e24d8d93aa1b99da9316c7780904753d4e0b45cf282b43a0c91bc9ff83cb25cacea2ea72563b2e759b69cabbb6a50d6a0a5ba545622e5ce576cc301ba35afadbfd1e26668782e1d741feed8aae894b564a425141442fb8470b325cf7c8e1552973463bf4e67a2ad58d15417e418bb91d2df4b1310a0a70ac744bbb4245efd2ae642609079a44cbf6be19809a5ff7ad6847432368c9749cfb336ddcc0e6f52a699b910cd24671f38af5dc39268a3c87771f07d53bf220b7c2d5058cc7b0bcd492abfedf9bb295ec304107130f0e98dacaac6dab998b511f176d48daeb81db53643ad194690b6e28c5ed2927e09a1e959c494b90db401681f67bf1e23fe9ef4c903f666ef39332a91a25c63efe9bc518e9aff61842007dda72dcc0264aa47543c0a8b1f0d25749ddfbed487282241140d4c64def1831c4d75ca975fdb03258ddd013445e08bfb479a516b011fe3a12e4bfca439407c0022889e46914cd41a4d92a25eaa57a55bc7337e5fbbd11584dca34adb5643105c8171e53cf04b1412c3107e72330ebf1b52f524b4e72570cfdc0ab179991f3782d05091ea57b1a233048bf062e88939cfeefb61e8beaa90395faf61c4d974b23723a4a5cd39d70f92620f8f4f27bc99ca67bed7fb6e594913991ca3025480ad791bc94a0def36fca491a206440ec31e32bb85850c3606c875708309be63c2b4f5c477521aa08e1d059cedaafe4fbef5523b79f88b57d0c81bbdd09202095f10f13e2609e833ff41b862b2214c22e8f2b04a363b38d26bf95c07b184b9f909ad3a92122e158d3566d2204b22d4f2f3ce11a65544ccecb01a4a5ef62bc969fddcb648224a5c7bd94f8da9a7d4df393d880f537a377888874c19dad357a0564d303a5c1485c1451ea55d68779dc0c11c7c38025660684ba3f70cbbab00d15b34c0f2342207ad548eacb32ebad95292e85211a8669b586d05b0d0b9f278a35ea4d78e97fd5dafca6b72d8e1fbf3e704a60a8ec60befe2e3e4d3d37f9d33a0feb88add59f0171ddeba0b79a52feb9a1f4a7a6ee7c6927bd10968fba788a807409346a0fccd4f7daac3c8591fc689aed881829d479e8d360cdb5819d5eac718a6f860f2d9ef6a0d36ef6e10efbb37819bb7b03ab7649173447b2cd47f3433a2422b1611dff91cee0b10c6d060d4e84a9e3f4dc194514cb67f1e3985be05c845fc92b41955d0f61aeba6268789998bbf341a8b37af48f07b13a676a11d27330529cabcd52365842be559857cbc2a63a4ea1c77fa8619040e79705c5b51f473e13b73fc09c28598e070dbbb63ce884c2843ec365d4c5bebbf815ee3314dde0bab6b0a71a398e2d9ee8ba2f832863fae7eb0c18adcdd17f1dee0df29a8409acabf516c8e6dfea5a264c1c6657f774c86a14cf96eecac18a41b1650a9e652c6c9264b03aa2fd30e333a9f24cd6b0313358e3c00943a1de63ca970b7da2cb8a0fd1109cefbf12176f5dfb59457480428b194e88449bffd8b8d87d05d30f9ffe9ac3a7442b0df3418acf9165b14242489a54b6b47ea543fed5de74a00f61ab2af553b60d8d21c76c42052c72e4841bd94cf88185c39287c04d05f6336ae581cf7528a59b2874795caca79f5600ac64ad5820a91c711ae5a1c3762028242c5c8a9aae89177ec4db5785cd07402d45805a2e2e970059e4e6483074df1bcc01f57470fb66f45ab475ebb5343b727168e355a6c25d42384e39802d7b4a8c54ac94d82de12f8de13630ec8c19f008f98c505dbbfb21b363472e23d0147d1ac555f0981e2bfd07c62d097acf930094dcf239a40699421b207ac2575b7edf9b1d772ab066362820c182c2c5097a47d1dd25ca9e0dd9c3ae94e9a8f0dece74cdeeec3a17803d5e11f037820ea20364234079286a7c291f3424292b0eec3e956513cc6b078a76a3b8ab42c5fb5efdcea1d438f7ae08507275b48f9588a15be763ad094885269efa7330f6fc9d4746997c98d9f5feeb6dff2734d75afc6a11196b35bb9fd0c0af428cacef0df2c5ad4e5fb4559f0f93af2fafad6fb77f453238f409ec71a912350d7b62952e4858927f620d31569242615345265ff1cbbd7fdfaff35a45732628da663bfff3d3af3d7b537337754554458a2d1af0e16aa8ad9436096f42e243109cad32fac1adc58d714cd3d0d8483c783006991f3da263ef5ae1ff2ea06584e45849d64a07170675c29f0b2abcf1eedbb63b6f5d9dba600996c7d5edce9ac69448d05c0704fb9f84e831b60c376b8a5d33ea22030e2dd3dd421d8e0a810a77c085a3861fcba214a8baf592d624d673fb34f906581d923d80b06186db8ce5fbef2bb750166f7556adfe93d4951a825d55b0bf92c9f25776df784f6aec808ae221cc98d05ce988fe6a13ff96083dba15500e149409b54345274e3633fa8f6685d6fed40c20a5c5705f8b37099a5949846ca15def5a6a427eb4eec72747f116366adcb9b74d3de0b125bdee23ca98ee6312f41fa3d9bba43a8d343552c969c41f766ab4341a42ec4cd6f4d1d4c4b1f16979e5389fea36a150580418d95520506fe0cb1a1d861e09d21c57d88c46e10a3c5ad1aedc8f2743f5c06f10d6da9b2bb3ec783c6f5788ce9400795022cdcec197f9dd3ee4cd26531e7f057b6d9418a0c52ecdb35a24a2a079b3d396017feca8b31aa55e3d5ef79c9ea9ccc7e3d0b47f28f273276666fa1763b3a452672fedc94557d984c3353344a8bc9fc833dee685e33d63540d0801d8068cf66cc48ddcb0d42cec881eae36fc2614f96ad67fceb5c98ec33fbade0e3049178d503c13c2d5d71f32f4582d1cb0f47a2ace578b903796768a906998bed2995798251d7eb92faacc19255bf12c0024a94971c185841113faa288beb7e58d4a98289630fb3d230f936eb1b9d9c7b94b5ac9d3a211c0b454a26e29bdffb522548a65e8dde3730918fdf0575245e71ba013ce08f6e698342a61a81b1355d2483e97c06462cae1cdc7787f4",
-    "bcee4396a08dac9c14981f2a8f4614a31b019c83782d5d8370acf9db467d9d95e8efbe44274fefde5860c2333cf81593a2ada9f5bb6c2362ba97fd7c3e5bc836c327c66b57c0f023efc0c0ff6feae0e625df2f4e21057060170c844c86412700d7d337b1f7835a0dafee5206cbd76104c5a36623c7783213f8dd457b5e69a86b74030a27b3c30074242b1d97e65a233885a681ec5a8532bce9dca1998dc32c6b40dd997b99a6ed6288e0b9b09447e356bc5345b2133571e65d47db2c736a391970879103d4137cab6c0724b8e67064167cd5521f32135fb6ca43c1e118adcded8227c9dbfdc18cdce154108eba5d8c60e5362e8fffc5c9ae6ac2572188617e4ce0f432e2476c74a4227af64b58e0ebafaf0b1ce01723ecd36a2a4167b7991e28b6a9e81992fcaf7b4b906d0361add02104db83914f28baea26b50561faa46293ba5247e8263ac0347509c36405747866d2fa2beef44f366108f6a4047e282a477c28654511075ddfaad9b9844e18e67320a831e647d923b2720d65ddd9ece165c222231d3c3e7f0001d15e3c690e9831ceb369a8edf183133814bfd20dd25d50973bda58ad03c4cbda8008556fc653ef401ff76ee858c1f79a0b09b4232768e72dd06e42078923d5647cb310bb644feb24d6b7e9d1167c3676cd96f79965a066aca314089db60bdc40c2be4b69c569ec76b3bb74a43fe731bc869c9222ab5404304a513d4f7d2ec5af278f7c3d664fcde579bba7bd472bdc00a1eb4c46ff69fb7e45e5712919e8656a8887afa28cadd66461fc57f53d574c92105818a89f210d7e8aee6de2e78228b2cb03b850a6e77627f70f51bb919bddf61837a978dd4cec2db138c657214ac07b67134bd53b071e2bffa3608a0b0bac88b0ddcfc1ba4dea17191c9ad76ab8de72118893256a7e13e15a3bf98bb5757a78c58328cc4b380f3786f22c6be81884d213ec3cc2784583a47a4003a59ebea08bd06e290a892c937448e664dac672942b068b839593c442f6e1d22875e01859cd24c17d108696a3196ea4794ddfdf25721d3dd3e754d1ea884e5086479819452991403a39014297fcc734e56f8daae4d49d5c47016fc3ead550783df895542229ff3b034b5b722ae2a2b04ba70e42c174e9ddb89ffa60024aa16f297ac9383b2ccad53de4bbe4ea2fa3fe3d059d16b4b4fe9959ba3c4e58922e7fa2673f50be5b636ee7c79b445471ddf5b851ec3ac505980bb184c8fe44c7776ae9aab4e66ce31fe1bc00efced390a82f96b4866e31ba3ff832a25b1e1d00ec44bf525ae523b7102ba60c1d3a2e2bed004524afc90a064b325a258eb36315b1496c748f5407e922914787acb8b47bdc495e521518e0637eac4b1b4fe1adede145181ec7ef038d48c473d6f296b349d7cf874d329c71f272883eb7e77ff303957e159fd417d5055d82687448950dd149e1074a1785518ebaf7ac167e07f1f559893a20d133b59aa294efebdae1e19a30ec9a3e257203eb9a854096395825ef4d1e4ecf1f8daeadfa049ea6c435c50d67fd21c6f6b11a8be46502f0dda1715f5349df5330454316498660b7996432e679c73f1af33e529ac669496bde538890cc093122842e3e2e4bff937708dd4b1b1d3fc066a63824266461e4af9245032d690aac0ea5636c29606473820ee57b112e2bd68c0ce1936b7e76a7873cad678b26b560d7bb10a7dcad3f69bbf226faf2f572c105741a121fa1c55ff30b2d0b7339ed9aa4c9a3671e6e4b572800afcbc8764b16f0a61c4c1ff24c3b64992cd84f39d1a4d5532a7dbd9f7bc847258a33c509a945e53236cbc46b61fc6fad662c523eef0c1eaa4bc0a49610c8d09659e7bdfa858d2494dc3da0a54fcce229951d366fd17f4120f27ac77e5e6b777693641a853eacec09cc4dc08ff6ba22295acec61c5e6215eaf2a3a012461eaade8faa9cba630c5ce2bee6f1a4676d54b4a38b7b5cfb6c98106a4882ed88153a4f0bad3e0f3d04dc1ae5318e3b8f4ab1d122a548eed47f70edad1a164a9c5c3eb10fdecb24b0b68005b2e958980481834c4f673478d3f47d07836d3c1c513dd920042381f70f1a68671acee2fdd453a7552eba497af27127999a13a33104f0086390e01635d1a0b79d92dd43211c74047804e82d9ab26f97ee88e664871dab52a2a79443e39f06a6e8ac9d5e986252529b389d9ed0b2f55fb16ca65f6e90cc9a149065f499630f973996c1e2b6c53f2ab391b7d78cc6926b1684d066a3a74b86b3b633baaf3730acd28deaf18fb926e1ec9c1f8a2345103cc4cbec05345db57c5adcf062412f289607f5fa41194f69bc2f426a30c7a6f8d1027ee8dc96c9957e90fbd9b16475b82dfd8698195159bd7b4860004beb1fa85e6843eca1acbbb0b8c7ec0b865ed108e297a2d5f915304167e18d01e51497e6e3ea76ae99bcb849f7595fa74c2a6263e2bef65f1063bce05483980ed51eac5289307117f17e99d761337e9b1fb625a1b900e6179f3b02de57a0b5f52352298c8a2d2c816182ec169d2b9c0490097ad98e2edc99c6df683a4b5b6eb73ccee0aaf07e8cf8f2f632381ac407c5c578bc1c5a8d0915dc231b01b92dbd25c2bfd412995780582793736572f1e23ef690bfe6872c2572285cd737a4be91f4dcafef09232de77b315d73f5beb23d03625e031d2438081222b063c343f52565ae314ac47a4fe518b45d0c12f2ceabc5e05c20f607b97035afbe0e29249e47961d9cf9b385c065966b0c7ea91cfa9ed1b55a58b9aaa9de080ca05c6405fdf15bcef74177226eb225a47d532bcbc82a5ffed7fd86c2609b146d86f566d0b84f638d46d6eb696bfbbc62c4fc981a94c1d6ccb9f3f7ed7976ea7e8ff1d2a2d79986fb27f1401f25d5a83f64844fa9e839fc8855007b417b261d325b6e7cb124b27ede8ad18d2b6da8bfc4d4f50c3960d5a1c82e4557b16d05471602d2a31462e4bac9535c9a57389ae0613a674815c2ec10c19f529c9274896dfe49ab06889da517d482145ed8f57ba4b7c0434ce24090ef2459682a4f6342ecc382b4cd3409c3415bcb7f1bbaadfb7ec308eea8b6cb2912469b707c99a55c1754db0650616754735b85a41433a30b28e3946754f90caeb03c7579fc9982e6ec5501d6f23e0f2b6392acc435907d79ea11eb6955723a81c4f02bfc78e2eeb1d0408f8f06b4d2f6d20d90f7698c4e58bcfa993884424f8fcb602ef35d23737fb6aff220927e28c19043ae708fd9755256a8a1660d9c5827bab1b836a10aa23aea9c92fa3b25428b3791c5d25f3f1b63befd5480ac4192c966350edceea8938ecc608e0f063d16d427049ad62625f5177470e7a0d811e8d4273aea8f7377d51db07fc34d9f18497a0c2b5c0bc5e8778e06bf7460f0487eed54d661d74346eeada9090957159b86f8b68183e33d0c3fc134d87e068badd8789d4c7adb829fe08e4558bada5ef3f526afb2c7b6184244af0d07aa5cb525c519ed32298bb6241d900ead0532b0b1fc77a6577963e7a44627ed326741af254ab957ca0298a74323d2ee4f1bca70e20ff796491424e108e03c20f2eed7374c0aa2474a91f3ced6f46165c886a510734d606ceaa08822bfced69def33cea3662512fb42ecefee341d1b499b826ad882542374b032e907a7e6a4dddc4620a5d1002b5aee25711fd2dd6e9d4e90ee350f2889d6c1f4328e4b711fc919ef3c655311637b83b4eff39c157e0510807ec61714b843bc9eb22a0f4dac7e5cc07b8e9ed587b701d9aca2a239e76ac9a16338b74d50578956e06b1ac35ee3b822ca779922d89de7d915afd7d80831e8534b8f8a2eaed252fd862abc99aadde62d4520d9a7c3c3da86081fc36927de60e3479096a2b5025b9a789da01da969cfb0ab2f252c82db9e6663dce3888146b365080f649cf94f991312817147d8f0d1774d8d44ba4afd846060df2de1d1043659c3b94b1eff51fff84e5a81a0c635aedcf677285e0d722e3335449fd0f49a41264fb963ea5bba31dae469c789047812071d8853291fd8003cc31a8968ce7acb68a6e0172ba6ee0e9dedbebeb62143047336c5a91c77085afb01fc075938b306d7e36383ecdfda55b9b5dfdab53aa34000289c398f617a146c4a06404737600484d8d4ea960061ec2cb575dc485f65f275540d0ce7550da08417632b6f0f7d044f6f719ff839aa3e5c9db94d45225a1cf0bdb0c5bffa781572ad605ad37aa988240858c9493dee9f00ed281e93532d89aba5e9e59ec430cdd5edfdfc2ef65e094eeab71cc40b59c997943a0e0dbbc80f1e11834bf3b53153ba1c1f0ccf63b3c802439b2ef1430be6994300d9b2efe4b84e25bd3bf8a566d4851e7fff57cada544d722438e8980a31563ef0558fdd8db9bdd6f1a3e34f06104b680f63c1f80a08ec6ed74bca69bb1023fe63d24c7e7a14ce85db6e21173f2ddf14f233f3787a37e4b347e4d64907fc0a23c3da017c81c27df9fafd4695886d0ddec8c47982912eceef886ab5680a130bfacbf3c67bb4f0cc118274bdfed43bbc2ba56f048d6a390e48932469b30ac84fdfc2e812f32d00a85349bb22f2d8091e64282fca1b40811db756059de5d03861d6a22cfc6289097d23c26c5e3f000f9b34a0e1b28a1269d8673d09107b29ccaa1adc8939bdca312c69ae4a238f45410d8f1b27392d594ceea2a6b42899ee5c5857965b29bea1bc413da618899b1894f2adff3b3a7b05a626e50e42379f5d0e0a148ded33d815f59d1401b197a85656466eaf88ed30d1ad4a87985570291efbb3a2c6f22c0b111e65c843ca3c6179e94335f0f91d4696e1a31107948a042f55f264c32a35e719668483957c9c8e13fd01e5f751870a509f5f06ba41ad63cbd5f706f25b1e598f6c9709ee6bab627211bc38494962e930779ed4ea2a8471d309c4c4f0603238959cb13476b673489696c87ad9da5fef0d6467145a77ae0b1089c8626988278a85be3292680d9d7e4c6866f19b78595d611f15f9a5e37b3d145d5aff4a5b58a3286bd25a862904817afe8e9b9105584af15f54554ca5e7dceaa0fbd1111aae126d74f68bb6f0ce98094dc9a59a31d9526729efa171beda9ac5b7db9118aa94b9b5ad58dc20ae1c328e31269244d636139\nCiphertext = d248b9e47c303f735b0d29f6111a742d93509ae051466688d56b587104a74fab1b259da64475fc0d2c3e28d87ca4edfeaa5715c23dc0e5281eb0c0c14e22182bb02f9f7d3c24555cd6a3ff766c774e67730a920db5f85d47dc23bbbee460f0922cd7ddba81ccbe727b4b489e79a19db2d012dad2a732273dafabc0fbded3c47dbe5b6b585570c39eb62850dc47f4aa0c29bf5fadf334041fdd4658fa6cc29a81192a53dcf47c03ddca9d03b33b06e5b3808be77925b7e7d8cf51fa939e023161d969f92430917d73f3aa10b83d5b7402410280561a27c376ce0b5151a51be2ef4eb9057eed25a0715436233615dcad1559fdbd81042544441857cdf46d72f5f50ee552cfd3bf166c530e57fd97f34e2e71bff8a90b30b4c4cc3e843b0f06e4eb2ff82675e428f5303aa9141dbeb615cf6aca5540fd7cb756fe5f9b08a4abdc6eb90b2eaef51c21eb9ae79a0e44b0755b3ed48f5e6e57f3148ce02501528dd3dd2b0bbec2650710a183e38510990002ce6498dc5ce7bf33d699dd18b66c0f8031d958b11d678674c355a635f4b5e8d863785f5dc2f99eba9ce74595493c017697344b651dcc2a0b1d5386b73abd8bb2dc77a2d92173d3688d0d704da9e44a6385af9fb3a81db68822b1eac9ab284f0155c20",
-    "f6bc34af85d8518d0dfd32fdaece1379abca339a00e1326b624b3e4050be5db8dced5e6c4b88b82b6ee2a48c373d236ea3565ecc072e953ffe01b624c6ecbf534678aad9c3f8a07d7dd7232134b6b397d0c96ab5f795f9e3af65b96e7a765283d8081dab9f953113abe06e8d150bf9a8416d8932fac17b032dc346be43736dbf066ed239328803510f6f62bc8abc92f6df9a82c02cbf85de91739bc8d7805d392341be99798079419540dc952fa0d3ceca4b806ab1db3b717f0d720038343465a8bc0da8e8964e58634e8a2d6c99230af2ac7c89acd3f86a22075dc40818028f3c632b36a39c0e064e3ca2a078c617a3e73aaea56ef11114f9efaac90a3ec8f8d9b18921a80d74b09ada83efee127f41179dc6c19c7965f3e7f43e22f636534b123e9246172f9920f253d2a2652a5e8c337ff93b2d479bef5e96e972a9b9cd8af057c750bd711010d59ce065ad50fdd487b5dde616301d0ae6373b6f9efae99d8972f242dd7a6bc61caee70201869be202fd384a992478dfc133b84171f013244c5d17585934aed3b43b818926246227d255bf832ff481f5f8d074ab159a11d6d17ed0ac50f727b870db966e0373bd3b1eecbf9ed66aa66caf33ac57cffe4ac6df3cf7b0e54ac54be4f3d50f61b33557c2990c908a710c85000ef6fa62716960daf918ae3d81ee60b3813e65673bd911ce468510bd230b9c2d215afe86ec12e49e0ae87e4235baf3df237188f5e0af2e61c22a4bf77190dd5dc804b4cc330b360c3dc093ef208c37d299ea0cd2ba906084011e16ac5f4fc9646538d5b538a99546d34a4599c8529c1524fc4b394d6a9cd762855905233ed92e72c8b538372ffe2f0df7085eb074616c7e695d7de40779e384d5fdb49fe02385424ea991dc05c6ae813f76c673eb45d6105bcdcfdbf04dcdd20caa6e30efcf3537bcf72947e1ac37d1e8c600ba9238569a4b3afa590d61acceb2572da85885146a142c8f8c60afe4d53ee4d61f33c47e5a99da9a346bcfbd013754ef39a4d7f16c4c5fbfa53d7f180c16e2b64f97dcfd65349939b5fc167c7a78926f638f1893fe9a81e897beea3258a4175d14f41dc123ddd846e45a87f35154db8a5ac27c7a0fc95d3b3113cfa9e7c828f83a1f0d91ab7789c33be5f55ecbb8eb0c81bcc0fccc880011b21000e2f10773388b198ce79c5d694472d3ef6b2e55c342b29c70e4f33fe59e2e0bd3f9ef617733dd3329a0e426338d9c007bcdd382522cb96e59b223825a39b01b52809f5e8518f64b81c99022d8215b5c435d87cc1a57bd440b31a19b197b277b2072968595ccd64c135ae1b218046e27a7f2685d013ce3173efd07586dc72a28ac4792e804d44f9efd785ef005213df928560a20daa4c24ab07f081479270a0dcee3c26331c48a164e4b9d79a7c30c77ab06b00e9b72c190d35fb873bc095d5e6231a89b52a0737a99532079bcc72ee221b48f0d0d9ba9105f981beb4225f6efc1230d6da10fd2b58a65112a98e4bbbe1accd6e8589eb6d9c771bb911cccf42aa6cbc68d1976f0da7eff1e70277e8c5f83734ec1efb2b00708fef08e986bd6519a0fa4b5772e585ac8e37fd2a2af07aa382579498b3b75863fe792461492b8e71c4a1a2f4421705696a96601317cfff1632784b5d75fc2036ed3fa650354620781b9fcfd53f1927223fa045edf4abe7b2144512f3e3aee99f7ac3e46028bc2427aeb18e9cb40db57b696ea884658abc9b7bae0d8117f93074a3ef903528f8b55c7687cf9f0119a1f246cc9e993219c6384359e7e5e639bb294b264048060224ae168d7b9f1f795c07eaafcddb10b61ac2be3ab3e1fddf75c1f47559f38d24f0c773d0e8bc5fa85d7d33e3aa8d0f15583b8c1e7aab6f5d0e085b7175678bf11cfee8eb069b78220377819e3f4d28eb833d3d21efff543d5c6357fffb4a8fdd6ce399fca42e2d71c53c50f6b20bcbaa1650b57ff483837c39a37d5e978393c332b43021508b8ef27773164d69d0af3c0dfdc125cf30a7c49a7d8e5320d68a35e80cdfd62a0b7ce6a412f08c8062e35265fad5d1f226d590e9b068d09e48772711d7dbd786a38c0325b3d5665c2ff45ad0a20c174dc5739896ac727b34f11c7af299d36d30c69bbdc35770138cf891cfdd8123489fdef2dfaffa9c2548ebd60b0f0bcedff44691979b4e92b364753120364dc2e3b895095da828e8659575a85cca587ba05ca625480f977a6fe10181ab6ce005defbcd8894f8c71811909cd6b56eb7ffe327f46793a9e98bd7fe8951400276bb9c7607f8ba1e633034b73d7f0d040197c3f346394eba68c8accccefe05f59cb7ea9ab1ae2e172d8f466ee21c6531cec2c9dfeebc477a6d98195c28bccc1d5e23ae50e3a1ddd7de189e36ffe0e387df7be43427b194b16e18b42eacd517bba78edc9f56a2c7e89e6f13513718869da7c8c529bc337217a69e14e35cf97ff7db2c23700347f0a33ad25a299fc52b35f63949735ad864aa127053797541864b07168f89ffb7ba5c9a8bfbcb4248383a95f45461a7aee9c658c5679205f47144ba4a06175e746037b8cb6556f06405e0d537d0f2bcd898dd5fb987d96dbce33001a50abff5b9cb0161dbfe30f5df5a161ddd8a750b0cb33898c110415881fc81239f2e25440bca41a5bc46fbd3787e6c8fe8a463415cd9a82be368a02566da740dca8e40e686e1213d9c15de2d3556a1e1180b298ba3074b4ab93e469dd9a39ac0c8a173b04a5ad913e72e4d7b5ff520f108e1a1747c11b6b2fcfaa89b3ef7e669f8ad9620364b4f4f0f9ab274e76bdd631df033357a24723653e427324d907a9eceb3c375c43ee36cdeb046a6374be19ab04922da93d4dc07c5914df06fee97dd813f5fd501ca75e3c5ad53574837f2e51ba6a257134e8ee0f4127c59840ba8b1bb13592dcbe47aea50e453c7837e91bb12ad1c74fd0f149479bc0334c511a822145690a3a408caa32671ed05c2dd219ea360c67727c1fe6a6cd842301761e94bedc73f93de7091b8b6d2783a788313b2fa12595904bf5d1167a5ddc4ee151b1522de60b7293b72a62c4d08b396ed682b6a6262a212ddc8c70dbec1a972cedc09f593e21d843279561884f9759a593da7b17a147db7559f19d5d6f43ea98012872f974306037dc0d344c55403b35a5903f766359341bee5bccb696fc0fd1c7aa8803e4c2f9e6e23d386d3a202027c5792e355592efab9330af330392a7c91e3cacc4e645359edafd78b77829374cd4b644817322b7650696fa763a0cc7143f9ec7e2f6ab3c9ec2443b0c0b0a31e9eeafb7bb8c375232357f08256959a10a6d4bc98d6cd9314a2ce7feaa8c0eb1eeb15047f715d6ae9ebd64238d648ed6bc50617a360d8ff9a01aa0ce0e29338d34bb9612751445372ac6d74837c7d2d67729760216ee33476cce1a154086ec31d986cc5a14e86561c6929554fb280646164bb03e8e52588a1b947960a77d61c2d2499212a742e1a5b78805b5b64fed141d3c4834301b8a8bef31ce65edb539fd9469b590a6980d0d1bd29e34a09f87438059a09b1ea234d1bb29882e67599fc1e417db9d86332077cfb05fe440ad1243e26a67a0ea30e63cdee8850a543d76e810140547412fb1400ac87a10e3bc77d3918750a5cc3e7a0efbd736c7ed4139cd5855ddba47143362bf40b91fcbf27222017c1552360466483e67ed125745724cc713c713dcf7ef6ea3081d65d8d78b903382717848bee7410431e1040ec92373f75a1bf229816f55dcfffb6e6da33ed8e1e8b05f9348cdcd6938f053eb9f93e0de639e922627bf61a6688f9649bb9cdfce6236a176db8b9b53ce4b5f9eb9c0680c92128bd327aa7f04a745025faaf117a18d5664027ab0e3f5898b834e1a75cd4b4087637733416f8bac1ccd67cb4457005945676d03f76fd0453fdb9968643fec98d28da7c8cd7070a803b14a2459f073ea075fd023a896d3306fdabc54416e95907103cd2fb642e301c71cc48e8eabedaae356582761a14e0b3b0ef1de06002c2acf594c85820ae3a094e5b4680566b592221543c1dc5192d6b208e86b5aca91d4e3454564eedb3b8208169ce97e1632b864f1d9d4c4c4c0fd4bcc5206e8f6d64c7cdf212d718cb5b7c7ee21593ada3f33f5952e12bba4f46cb99044978fe75349c6ca735db35891351d7e5f02a93354bc45a9ec756453f053cb87430b3e9211807f81ad99b6fceb8ef1b2d655910e1f5fd22f2ee90e42abab230f8f39a8345eed6ad294a0d32416a253f829093ecae209bc1dbfadae04a373080f9ea8394a28ddfe1134309bb53ae571d2019ff2bd4be94f8176d90987fcebad323f0b2921b85b2610852973f383a2ff4a5fa82a77b13cfd50a33f29164a9ff409422cc4cbd772132856cbd08470b220ace957a6b8e02c8003d750539a38a8df19a5b662907b72e3098d77c2fc3ece0693b47ff19ce911a93b6adce75653d48ace6af10b8f1141437f9206658707b16794e349db3f1a02606ea167d0213ce3644f64ced64de3799b1729210fc31ba1811b0c226306f2466b230ae35e6d8fa11c8f932e27da8cb1bd311919bf9178ef08bb7a2b4ca2d2e6e9585ee9f916991cfcd4862f5de9fbbc63bee6edbdcfcec9173a252eb59fc6d6e58258ca8b2a4475acfc1e09a0c9566d23d92e9ada97de51895bfb0867c42025c8d089c65bba67f4dd84d7c5155a930329345cdf3b1d6e910e730df273e183190beb900344bbce8c3bdb13a7e4ecbe967a61d47921aa55bac2bbb24e3e03d386ddbfafb3b32235b5ed922ed6ac2c89ded1316b69079b826507d708a6cca14ce2244a67be90fb91ddcb0c97432703729bceb432bc856f5eb9d2f169800a04283b080f0e053670a21468df9414fda9f4153eaf1669a19ede7925f832280800f0063ceee34b9d3b0f8da2012525fa7927e76bda71954714d5f51405b920391eca2ad71160acef4091878b907974573b4cf1b377baca0340ab0e4ec546fcaa6130603ad633c3ef980e88d8f44ec5de743cdc6cd9e0e4cbdb97a5c076be9ada8f26bc54d711facec16a2401292cc167bb98cdd320ec9321414bd97498f6d9b54dbb45ffe4b3e3f88260657ee23e19de48a93595c8e3a289a02d76a27ceead05d591633464709aca117c26aa49b64667f2a3b6371984f813d7098fae7a6ba1841775b52314a06c80b4c994ef8100e233ab3115ba2c39b97f2d5082a145720ad0b12b8a7cb275ba848b3fae14fc0c82bf0353195c056b302e508982f73a8519cca722892482b9d9e6a58bfb4d862fa393eabe6aedeae1be5ed772ea3c94a0df1d9684a131c35246c68b32e46aaf89f3649e58b2e99bd6bb3923d3ab43cbf73b6b3d19fe3b62bef178f46c79ba85e23ee4b25bc561e8fa97f51605bc0b210b02aa28242e81dae9489076d259f17d25b93b0e8a2010584d907314e3bd55482f0fa43d37ae9535629d28d6f837360bb35ec869d2a959789dc49b9c8c515942a1e03650566b736551a5180a60279bdb0ff9c387beebeb9e59ed930b3746464a010a6f7ef1de3c7d76fc6899b1e5ed98213813ffb333d969ad72fd8537ef4e12ca7b78d35c24f44ac82da4a7116492ca2efd86ee6a4474014e72a5cfeee7f729b77cfdd1a5d10a03f3cf28f1d314fca36d31ef2ecb3cfccecbcdfd22367b0a0e04435654286ae3d4fee13f56bb7cdab40b4e1dd01f9ef857f94a67c1e237e24819949935ff3bd73b0461ee9020fd0a2db2cc6312ace97e4a8a33c295271453a12822db8d1438f22ed0d466150990dcb39ed042424eef7a12",
-    "10c83224c856923e3251484a81a15cddb4d7ada8bb7968dcc8f85e39ca99ece8ce2ed7753fcee6900cc9b7b5691f2d67ef9be13f70d195bbd0047908025df01b4f4d581fd59239836578627d9d585ebe9b053d807e9d3ba25405029a148938a746636decdade02b1afb5ccbf2f0e14a27c98a1e130d9208bbf7da4bb4e572927eb348568921d4a3309a2c24f367c935c2a8e1524c3024ff350ac7da8d2849586817bc9d46a08a21aef035a6151e608ed93b1556a484e455819f9ac2fb155020738962e7255a82a0854b31fe20cdd351c10a33eb693c9be1a51a932e04d0364ced41ee1bf800d0c12ab5eb37fe52563666e52827720e856d4f24eb06e0aba446910aabbe36513f2274362fedba4c19398433029495284ccb499bb559a9cdbc94a0d1b733136969a743945a04e1d2d4e77fed21550af35f22651c7de802eab7a3942d7ec55a3a5002bde8d5cccc1d4ac4bb7f4926615fcece543fe5d9092d2c4f50d94fd9868775a072f4a5bcf2e5fd10795f7f172a3341ce33505ba68e7ebedc9c1e9165864244ed31bbe5c308dceff858cc42010ad8c281a24689cf2dee8a549b1abab9981d70a912174944b403ce664d8608b2f723150f5c12164e4caf28676e7a25c3928ca2a4dbe96355ef8f282e57888d40715df07bd8b5895549ad957e758abf868def1c1f5e260d26498616e2ac962bcaa33b879874569f198a91ce4e50fc50da77fea1df9f9ea900c834dcdd462d338efcf8e612aedebf254fac596507d175d30a90543627cfcef6852c7cda8b430e255c4d6d417de31eb5dba123e3ce9e2269867d9a94fdcd8ccac40a9451953085109f5ae0c3e04daadb4a2a47b0e176917660eb3c9f1aae0ec6b00635fa387e056623947c0621f0a12e86fac1881ed1dc1b9f523388d6b6596a152b3e732c561972879dcd3f0232ef0773a4fb195a90c3186c4688ea58967ce7f18386b80bd38e90cfd4cb899337ab27cba8db6523e979b4c449645bb2f320ccd28578bc7ec38f47225273fa61a2e5df97c4d76c556fbe2b0fd30e615f5fc82c3de7194caed9f5946c151c22b7a0c48f4a7cf78aa153414f2913c5eb95e3dbcea7ca544272cd13a1c52fa87759aeb430aab144fab418c835344605df3a044825965ca15de6ba0e59b2080f5844b2d110d71587e19acf14264cec2de5b8c77d18893215d1c1da0a940e7c2ee429a99e2633c216aecb7675a2314a09044951ca5a8eac798f8878fb5ea65f4ddccac53ee0c786e597169079fb6e8ceb37a71580b0904a97450909ca454a690821e249aebb75449e582fe1b30f1fa9f6464bdef654daa5ede6d4f223f4589ea25a25f4672cfbe974d51008bce296628556f55d26646e40b59f40e3149273760b40806ace3b5171e0b79865c6adb53513da2f24c4115de243150cec76107b48ca8da19117f00b5870e67eb8357e43c1b7b593c9875795d46ede26a109e05406b69fda988947e49ab195f22454c3c743c2ec51b91370b4df8d38653b353e51bb83215d122bcfa591009c007bbb6124bc590fed3f9c5699180b3b1424ad02f7c90a149b77d22dea5c996aba675c2a1a20e206d9c25d9446247d495a26486c0d0bfb09d0b5a1a177a09fa749dc36cee73af0116a6b779c2b827512a04ff0f60b483edbcdb33d2a18339463c498ae67ffa9da0aa3f3beb6bc99212f9e6961afde89045520b1f3f2e2761666a333d76030f443f53322f099035584a60978ef8b49f46d7d4d8c5c758ea52a04b59c1a3a1c2f9df3f3b6f5c45cf4b3547043b18c1d615a2c965c3918d090cc72946e8fd0b938e60e03464f4bc71fb719a1d173b0931930e58bf7f6d4403971d36b40f83be6b57244a7029e1d41dc908764d57a5442557218b509faeda4e9fcf31debbc54ae671ef636871233f29e0013c0e33933543f4b59df1978ec89b109c3977b0cf938b7f6166d6c93be5e87684a703c8b7b5fe1a8bfe153a179b55575ff05e599b39e32ed10d958699a1ffe07136081f0719b18c69dc74f66f211103e9c544f3c81a88ba9f66a9bc7017d9ca9e2cd97634052694a598476b99daf1cdfb6122869375ca5873d32d5c1e07d9b5b380b4f09dbe04478cfb1a13853eafacfed70c8abcd444ed095f78d07c0e8b4093be95c3aa24b2e5b6bfe3a06e9d2d9fedfcfeac4cea2490627e6da6a5cca383351952f654ce2b0ad359c0f7f4ad3f8d1d4a030a947d4a2e417bb79102729115cc8b6558c3362b1d805fb48ce4858deff97677e60375ed13e150a12ee7dcc8ccc64d9710c7f516555c1f7a1a08f0d7c6fd21f864fcf28c8f748c40494e01fc32006f977a5100577f86a484d11b82c90cfe6b4d6b1902fef486cc6f3e033904e150e67283e49a5382961dabd244412ca9657b48796e476a82443167e277d5a65c0c563a6abca77d316e5d3ab639a1ecfb1110af2d29f146508bd9874486dbb56328d6f59479e2766692821660462aa60b6bc8a710707ceeb0ea6429e5113e03c9f41ce0d69c7589deb547527673e8a9f9a9a74e9e4bbcabf2e306b35504c1da99730ae86e94cd047b2e6ea5e97e63a492430d37ec446434fb3b066adde08b17d7d903ad194a4a863d6cfe181a45c8c97b5062bf7c4e44d69c0d1a7e1f5029b805b7c21d1b5e56e697999a32557870ebaae8d87dcb5ca5eea2c5547a16b3f30ef9df8df821028c106f86e091050ff8b6ea4171e59dc2592d405073bea53f8ea62edf112dfbc7ca69809db8005783d63557d3d90d123a944be395c1dc3b5e1476dff188346327769fea65f3cf9363e88ed67335870ec8ef13eb9d9ff5317c4e24dfce9d11699e5f47b4233cc8f9d1b915e716a5730a5898ee65d30b1628b484a5e82eda95a590964a8d8bc89dd3c5cf6c4f9137b8c6ee9d6a692e0c0d1d858dd5b3c12de48badade4d01bff312c56ce3ddb34b0fdde3b0c2706fc292b9fac7e1a0dcd0b6534c968117f7de15eba84d2754e4bcb8093a5440297605598659f686075e2b1b464b6b3ec68abb13cde263b1c607545c45746338b9b207b5c381da690f653b35e363e1249551ad938b9fd7b0a944151cda07127bf9ba76958e926472f4aa1de8512ce834cfcae5414b226f23acdb1fe5cf685d2201b78167ad35fc1da282744c2a43cc49d49242f968f7e06de14455e7ef5adedc5b33184346018114e2d1fc7a5349e378da9b2af5b328c213888652aca9f1145363809eca7c1fd8e64a5cc3255418736e048a731f3053db77971f67014e6121a8e464833e5dbd02ea6caf385e43e9f378bfba657986bf852b32adb55e35a2675bfc8d70d43a902032a61f59f57dad2dd7d7963322136233200cb9a90c952074e9ba0fc0654f1b6fd6f7f0eb77c0fa6d8143213ce6e8b0c178f73e17a7c64839f9bebca2fc955ea8ae406a13b80a9045fa8d129fd859faa46fd27c48bde7b890f98ee938c0d78889f84181ae2f5711304fe554d4251bbc6437ced59d577a2a1f26da736193c3674adb13cef9f4cb4aa6585c4d6874b0309ecde300493b1642c595746f09e03977c8902f3a4a877db1153b248f295a0ca2f1e437d15fcab8fd77c5f967304efb5c4920b990674ae61b954af40be17a8559dc377c591b68067fdcaf2d27bd9a22041b981a84be3de50d5962b58f8c4a22fa05192c5ac99a0a9423284fe62a3a59f085136cec72cda2a53af106a2eb5bda28b6e02c299118cd91714c2e7d045346c78d9ed1b41c73231a21e42c298949f70122277f4134ed5c56639edbf3c3e717310e3d1f03dc5a94e64c4ce148bc5c6bde64eb80b17d5979892786a31225eb89bf9f5a582bcf65b83ff7aa361ccd9238d144f6a22a3f77dd8a01382df4ee90a2057dd310a6b0c4b81dfc92a2cc0c606d3be8b18fbe64ddfdf2004eeabea892be2f914edd1edd8e8829dc7704d71bbaaf08c41824dd0f4b34c9eedead9e10e53bfc6fc0bd37417de0c5c71cff0754d672f29c262d8e27b524427e12bc4e4705ab311d3bedcb1ddd09a3ca0c268c05c64951b7d724a9dafe4d249aaabda91d68633aaab845bf78f9a22d467c7e0c5fc70fc9a318b01d7492efea7fffd329d70692e76647ae665c62b280da0d62f870a52e4dc4cd92c9150c96aab16f8c23475e3152d4debb41b6756f000c3d8aceef18b49e295be7a71da1eeadf4eb96509d45d7cc42af4b7013d8bb445f577e8d4cff92770b8ba0e451f3e24c6d981efdb68c7f2dfafee40b8a425955796e369f0d4da3e998c1626ae0fa583334475f1fdde68ca211c3f2e9afb003f553191702e11f8b731c89ea26059ea4466f2bd0a1a5601025ca9417006bca5c9a57dfdba44c603ef9ad38922623b40feda036d84425c47fa42973e348a180a7570e1215044c375313ab08d6f521052dda415707ebb74d6c4774e039bb04cadc2799224bde1802e2ee2a018032e3a341700c0fa2aa28bf93cc479231efe7da0e9f68e572415348c08cf648117e9b6d1267fef6617f5927252c86cc087775db3e30180feb5ce7e1ac9c3761161e07a4853aa6d97e525aa88302954cf9390fde81f8e11d97a11c79e3bad261364c18890dd1f8fc71127edefe3571518a42be611a46a0426a33221aa25a0ae6514daaf96038cb59aaba898de49e3b215a4464e0af614e638c2d9b6e676ec427fc906bc516331a18121f306a5246d179e2d3d0f38ab8393f7ea5a2d24585e7cca649637b9983924a15483c167e8780f8dd7aa1154cbf731745a8d8d54a8c4f8d854371bb8172303f9ba3c8c7cfe8c378ee56bc35c6376aafe907d3294ee9a8786281b7deff78ff125761f1a31d0e8fffe04a52a7574eeb8679670ca3bfb740167a559488d4337819613d32752d8a89013622f6a8d70f3c64b84a4215f4b7bb282a2d17c36a326167e3270757b8f1d9a0137bfc5ec278e8ca35a69e49779cfc25b95a89cc18732b5b9d1986b18878c57e118506909207207ad0b4edf32fb2b35b6e70546f45d0849bd139ffff9d8ae547787e7b51403b54f110e2ac65468cd0910d80a4e321deafd46e9af19609bee1efa41b762b8ace989dd681503539e7d9948664cf7a73ffac9ce2a34b514253c4f21bbccd38057a6d68732930dcdfc9a32219b53339d100db0037a8bbd101e71f5054f3\nAAD = 7b3b9c07148fcd897f657ecfcc87e530191536b8e77f9309e8d7323888b3b21477f2ab7c885c105d9c29ac96aed23b366f9fde4177401b7038c6770c7bd2ee8b4335105cc0eab9e367f0cea90d6f1ae3fa76cd21ceb9f3500ce7fb4b2a3f9e90f900a231ec693aeced7afb6821391d1f5b1b957895777aa7a2b71d9571c00336f26d54d756392cdb74bfb67d5a621d517db20441f74d0940180baf613b09452f64224f8af7bbc864ab4a8434ff624d0c0646ee07132fd376506951899bde975df8c836ab4ed9cc084f1f6d500ad56345d2f250a0d6991b9e458c62b6023191f341c8659e8a38c878cfac12b032674503df9c9bb01c4340c709eb6dd7c74907d769a317f4dd7317843c47bdb4c5e1f07f2380d464b0c47269389cc8a43a09adba86f6aa8f44c8fe514e73b5fe8d344769c1aa20a4538ecfbf47562ca79fa497b0f02f103f75522db9ead50d56dbe86997d6085f1b5aa7a4cab9e51a1247ce4f724a14983b6bafd17369fac973c6be268e20d800de870928e100990ebb0d3bedfceda36c64be3a729b603bce677a49e8caf282c9159b6e3e1e775129bd30dc3f5c9849535d86a27474be03bb5749b4c0115e2614f8feaa7405cc69b1de479b3b57e551f876a9c8c57ab9879cc68bb2",
-    "ea110b2e77e59dd6a65eaa67cc4d4b2f4d6e646b2a298d3c80fb43969275d4414734e74726145dab06124c040656c39a94846e8fd58d326f4f9eafe5b95d85254765a21993f55070fcb9e85db5d42ab6b9464ce66de3f236dd2a0a26c4e5535dbdcd6eb350209a65aee785c6647ad4103d092a8ac932470880eb314f7c98cdff34fdf35ee2d36f09bd443b5defad7a5acb9df55965421fd043def6f4771e1bb27385b30ba22c0d8972aead6b654085a7dd3b60c4004a0dae22e25100e54e0badd0cadf909799329ddff699de8066dd6c3822d80c73c52d87e6fcbdb2dbbf852e37804b1256e23e76dbe43f30be4a577bc23c7941a3d708d1e1f579e9c6eebc219c74768168f6790a41f883790e08cd1e88ad09a544eb97b3d1d5af67eea666b9c027e5c7c976921189b955a9e605f6cc9c012c1c2e197c5b02504cb9ffbcb0f3ed778d540d5194fdf5d38dba6340c93da7c5501a082689616f337d8b59c2a92c25e777515726e1d7f6cc9552693cc7c30f1294b37f97d49814250d6c1e3eb335c5d214ef3641739d508b87106eaaf367902433a148ca962ec694409acb82d7749e1c88938ad382d0ca6e6cbe8255746832fe737c3e71dae8397f260c98d4a292a126ec21935c24096d2f91ae114194af659455d8a4206197495a28474dd2809debf5f550d77ffac2b0db521559910c352f23472d7aa9f4dbbdb158f40aa36912cbd918ae4c642e76d78d57ade1075c4fe1086ddee3d554353b4693bbcef1cfa87e49890838c36156af0edf384b0413d6d7aa\nTag = 51cbcf4a2fd82f221de1bfebf86a8c24\n\n# OFB tests from OpenSSL upstream.\n\n# OFB-AES128\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = 3B3FD92EB72DAD20333449F8E83CFB4A\n\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 50FE67CC996D32B6DA0937E99BAFEC60\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = 7789508D16918F03F53C52DAC54ED825\n\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = D9A4DADA0892239F6B8B3D7680E15674\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 9740051E9C5FECF64344F7A82260EDCC\n\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = A78819583F0308E7A6BF36B1386ABF23\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 304C6528F659C77866A510D9C1D6AE5E\n\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = A78819583F0308E7A6BF36B1386ABF23\nPlaintext =\nCiphertext =\n\n\n# OFB-AES192\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = CDC80D6FDDF18CAB34C25909C99A4174\n\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = A609B38DF3B1133DDDFF2718BA09565E\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = FCC28B8D4C63837C09E81700C1100401\n\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 52EF01DA52602FE0975F78AC84BF8A50\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 8D9A9AEAC0F6596F559C6D4DAF59A5F2\n\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = BD5286AC63AABD7EB067AC54B553F71D\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 6D9F200857CA6C3E9CAC524BD9ACC92A\n\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = BD5286AC63AABD7EB067AC54B553F71D\nPlaintext =\nCiphertext =\n\n\n# OFB-AES256\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = DC7E84BFDA79164B7ECD8486985D3860\n\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = B7BF3A5DF43989DD97F0FA97EBCE2F4A\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = 4FEBDC6740D20B3AC88F6AD82A4FB08D\n\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = E1C656305ED1A7A6563805746FE03EDC\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 71AB47A086E86EEDF39D1C5BBA97C408\n\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 41635BE625B48AFC1666DD42A09D96E7\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 0126141D67F37BE8538F5A8BE740E484\n\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 41635BE625B48AFC1666DD42A09D96E7\nPlaintext =\nCiphertext =\n\n\n# AES-192 CBC-mode test from upstream OpenSSL.\nCipher = AES-192-CBC\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = 4F021DB243BC633D7178183A9FA071E8\n\nCipher = AES-192-CBC\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 4F021DB243BC633D7178183A9FA071E8\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = B4D9ADA9AD7DEDF4E5E738763F69145A\n\nCipher = AES-192-CBC\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = B4D9ADA9AD7DEDF4E5E738763F69145A\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 571B242012FB7AE07FA9BAAC3DF102E0\n\nCipher = AES-192-CBC\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 571B242012FB7AE07FA9BAAC3DF102E0\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 08B0E27988598881D920A9E64F5615CD\n\n\n# AES-192-ECB tests from FIPS-197\nCipher = AES-192-ECB\nKey = 000102030405060708090A0B0C0D0E0F1011121314151617\nPlaintext = 00112233445566778899AABBCCDDEEFF\nCiphertext = DDA97CA4864CDFE06EAF70A0EC0D7191\n\n\n# AES-192-ECB tests from NIST document SP800-38A\nCipher = AES-192-ECB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = BD334F1D6E45F25FF712A214571FA5CC\n\nCipher = AES-192-ECB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = 974104846D0AD3AD7734ECB3ECEE4EEF\n\nCipher = AES-192-ECB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = EF7AFD2270E2E60ADCE0BA2FACE6444E\n\nCipher = AES-192-ECB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 9A4B41BA738D6C72FB16691603C18E0E\n\n# DES ECB tests\n\nCipher = DES-ECB\nKey = 0000000000000000\nPlaintext = 0000000000000000\nCiphertext = 8CA64DE9C1B123A7\n\nCipher = DES-ECB\nKey = FFFFFFFFFFFFFFFF\nPlaintext = FFFFFFFFFFFFFFFF\nCiphertext = 7359B2163E4EDC58\n\nCipher = DES-ECB\nKey = 3000000000000000\nPlaintext = 1000000000000001\nCiphertext = 958E6E627A05557B\n\nCipher = DES-ECB\nKey = 1111111111111111\nPlaintext = 1111111111111111\nCiphertext = F40379AB9E0EC533\n\nCipher = DES-ECB\nKey = 0123456789ABCDEF\nPlaintext = 1111111111111111\nCiphertext = 17668DFC7292532D\n\nCipher = DES-ECB\nKey = 1111111111111111\nPlaintext = 0123456789ABCDEF\nCiphertext = 8A5AE1F81AB8F2DD\n\nCipher = DES-ECB\nKey = FEDCBA9876543210\nPlaintext = 0123456789ABCDEF\nCiphertext = ED39D950FA74BCC4\n\nCipher = DES-ECB\nKey = FEDCBA9876543210\nPlaintext =\nCiphertext =\n",
+    "9C9EB76FAC45AF8E51\nCiphertext = 9CFC4E967EDB808D679F777BC6702C7D\n\nCipher = AES-256-CBC\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 9CFC4E967EDB808D679F777BC6702C7D\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 39F23369A9D9BACFA530E26304231461\n\nCipher = AES-256-CBC\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 39F23369A9D9BACFA530E26304231461\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = B2EB05E2C39BE9FCDA6C19078C6A9D1B\n\nCipher = AES-256-CBC\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 39F23369A9D9BACFA530E26304231461\nPlaintext =\nCiphertext =\n\n\n# AES Counter test vectors from RFC3686\nCipher = AES-128-CTR\nKey = AE6852F8121067CC4BF7A5765577F39E\nIV = 00000030000000000000000000000001\nPlaintext = 53696E676C6520626C6F636B206D7367\nCiphertext = E4095D4FB7A7B3792D6175A3261311B8\n\nCipher = AES-128-CTR\nKey = 7E24067817FAE0D743D6CE1F32539163\nIV = 006CB6DBC0543B59DA48D90B00000001\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nCiphertext = 5104A106168A72D9790D41EE8EDAD388EB2E1EFC46DA57C8FCE630DF9141BE28\n\nCipher = AES-128-CTR\nKey = 7691BE035E5020A8AC6E618529F9A0DC\nIV = 00E0017B27777F3F4A1786F000000001\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223\nCiphertext = C1CF48A89F2FFDD9CF4652E9EFDB72D74540A42BDE6D7836D59A5CEAAEF3105325B2072F\n\nCipher = AES-256-CTR\nKey = 776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104\nIV = 00000060DB5672C97AA8F0B200000001\nPlaintext = 53696E676C6520626C6F636B206D7367\nCiphertext = 145AD01DBF824EC7560863DC71E3E0C0\n\nCipher = AES-256-CTR\nKey = 776BEFF2851DB06F4C8A0542C8696F6C6A81AF1EEC96B4D37FC1D689E6C1C104\nIV = 00000060DB5672C97AA8F0B200000001\nPlaintext =\nCiphertext =\n\nCipher = AES-256-CTR\nKey = F6D66D6BD52D59BB0796365879EFF886C66DD51A5B6A99744B50590C87A23884\nIV = 00FAAC24C1585EF15A43D87500000001\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nCiphertext = F05E231B3894612C49EE000B804EB2A9B8306B508F839D6A5530831D9344AF1C\n\nCipher = AES-256-CTR\nKey = FF7A617CE69148E4F1726E2F43581DE2AA62D9F805532EDFF1EED687FB54153D\nIV = 001CC5B751A51D70A1C1114800000001\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212223\nCiphertext = EB6C52821D0BBBF7CE7594462ACA4FAAB407DF866569FD07F48CC0B583D6071F1EC0E6B8\n\nCipher = AES-256-CTR\nKey = FF7A617CE69148E4F1726E2F43581DE2AA62D9F805532EDFF1EED687FB54153D\nIV = 001CC5B751A51D70A1C1114800000001\nPlaintext =\nCiphertext =\n\n# Regression test for https://github.com/openssl/openssl/issues/1916.\nCipher = AES-128-CTR\nKey = 7E24067817FAE0D743D6CE1F32539163\nIV = 00000000000000007FFFFFFFFFFFFFFF\nPlaintext = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nCiphertext = A2D459477E6432BD74184B1B5370D2243CDC202BC43583B2A55D288CDBBD1E03\n\n\n# AES GCM test vectors from http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext =\nCiphertext =\nAAD =\nTag = 58e2fccefa7e3061367f1d57a4e7455a\n\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 00000000000000000000000000000000\nCiphertext = 0388dace60b6a392f328c2b971b2fe78\nAAD =\nTag = ab6e47d42cec13bdf53a67b21257bddf\n\nCipher = AES-128-GCM\nKey = feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbaddecaf888\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255\nCiphertext = 42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985\nAAD =\nTag = 4d5c2af327cd64a62cf35abd2ba6fab4\n\nCipher = AES-128-GCM\nKey = feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbaddecaf888\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 5bc94fbc3221a5db94fae95ae7121a47\n\nCipher = AES-128-GCM\nKey = feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbad\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 3612d2e79e3b0785561be14aaca2fccb\n\nCipher = AES-128-GCM\nKey = feffe9928665731c6d6a8f9467308308\nIV = 9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 619cc5aefffe0bfa462af43c1699d050\n\nCipher = AES-128-GCM\nKey = 3de7b368783bd7287f2b9b731814c876\nIV = 90dedcfff100eb1f1db9d935\nPlaintext = 8d766795cadc0961c0f448c62df3827eef3a8664599b3adbaab0cfd63875bceb8f992b4f7447dca10ddd716aa0bc4fe925e1aa3e3fd1d5c430c650fe3546d6b9a24d576a857c5f04e8c0a3b149df277aa19cfa64ee235891d3b8ec0e840d268b1e70dd8a4bf97628a0c7aea38aa21eeb8fb1a8437f2abfee05e0d2c30659e312ec03d30da51b7c19073a2341c17df806e27e796d581143d39e4de8d3f8d46aa6d6fc1a98d94fa69b92dab751d930cc12de21fb1a7468af09e3c12ff6c3db3967d10cf140bc46f17a16e24b010b6cba5ebf777341c52042596ee53008389c48d9690ab9f5625795c3e588f72f7a1670b2b25a9f4eee1c8845ac90f1bf47ae4ea4b607a50aca88ed304cbb700d02d5486139b0bc81ec042e574abf986972fa008b83ef22dbfe720c2f2f6355c87c975932cec545ebed657e5e7570c503e9aa7f0b87d0b2648e421ed1d34749637c95d1e931af8925236387e50454f0ba2e22ed05f90450fad46f4eb7ddb08656511dd065c0f852a7e42f618a961a6c6bec42226c6b6043580b009ec9837cf99844cb74794a82c269ff648e0bae9ae50256a0ad98ad9f5a35057b3004ac96f469f9ee966dadc16dc47616586cf242706df96bb2f7ee43d3bd1c65d2eac7b82ef242e77ab509afb9639e5f3995380e926305729ca762c487f4411ec2a9c688b8347e5287216dbb38c3fe2281a89fcb47ee2ee7ddf79bfa3ab61cd56a00981019bbcea8aa0444eb75958e5fa56ea0036d2de4950a7db886f4a318b433bd41e00905ab158171e0ef13172293bdf70064b9dc7b243bf9dc927589bf9e99468d1cb330639dbff1850cc51929b8971b0b2ede9d06bc5f6ba39d4551b587f09bf6f8206e8f1524f55714612581d6aa45d8fb83425f84a736576deeecafdcbfbb8670d14cd2ab2a7f8b7f374c07881b7bac2605fd5ff7ff7cf43e30cf49910961a9079c0343b8601be8c3e9fe38f49fdab0b7e1a8c1536cf84e4d80d26ae5ec37570839b5cda02929221898d611525c3a88fc444167ffc532b256cdd0a8f31ff08097d75b629fab99c9e1062d1d9962b211e15ec8709934029c4934e64db8d7a2f32e23dc541be306e9a57a3419115994cbc3a8f8d5ea2a6f45b9ea9ac0e51ed0c6680fa029f4552a6c8665aab00ab77928342e7284c321e9500ad4774ef1fed0f596d5aea371fe1793271aef38cde55547f34701a525526e83a72673385a85f44db511bc87ce1f831fc6ccf8204ca4f4a20eac09897aae93684f14ede21bcaf40a09c08012b92600d6a839ebdf8bdca7b34192c6c50bad8796b3be3c375dbae6217815d2c75cc878d39b4e842d4eaa5f5df2242cf230e44a240e18e47827f089b18bf880fd41a2516eac8e6ba3fc2db64a4bc28789860d7b18d9edeae8b3059f4d945b15d0ee27b1f74842dd1df117fe83a8fdade23a47c93902eedc4d33f2dbfcd1996e6dc1458409fde2302830e8d44c58c5ae67486b9950dd938f14c38bc4c9484fdc4ded93a0f90875773453fc14d428cd6e7beb0c705d61229d2b3df09632ebb30b325fefe2aebbf2a7aa8e4ad46277ca4b8b078818b63d04e7652057f6cbbab7c43ac355537e0d3918b4a73c00dbe6b30a27ee7a6fa213d3347ae478e8edc323404b8322b9c7b0173ed61c38ed25f3576a675d527d22edd51d6dfa5767560d3a50a91226338e8c4e6436eedbcd3d2efe9dc1e686b15d2f57d553abcfda57dc316ca453a690f20148f0dfa20c1c4a58240aaf7195095fedfa56d839d0230d55ce9a8ca1b9d1acd6fe98d583148ba0f4a4e3413c76e6ec57ddb79428d3a90079f64d3321c791f60d501c3fd02c8403f0f5e6c6836bbc96430c1b48e83350c3a3cfd017f15bee3e4bb1295d821dc98b85ab3145555cce2c34a8142fe50f8db19918b514a165d12ff6301fb2296788760ac0b6d9e3a57770ad5111cde5d24b6321918cb0b0887a282b827a8749733171914b000e7d3c0edad1d42ca60da37f0698554bb2a1749f73b3120dbeaa32951f8217a781a200467d5",
+    "b569d16f56fc9b7dff0ac524f03fee0617f4c692d94613b1e13b18075dc9f0d32811d4a8949a95f6b5fa46aeb83597adb409e68b2a0177c36dcc95dcb2e7dd4fb7337ff97c013364fe139e185014948fa698741d822044fa3f6978b16afd18138c845587c405ebf7a6cd1c28610ce67e992ed49e406658a0a202feed9709500d064b6f53eecfca57dd4b38363ce3aae9d59126d8ae7e140a373851188ae28c909181d0ac64770df70dd2475809350cb367825b59d521d5e457b4e36aea6dedd90a2266898b753b57fa359d43cd388e7d6c7ed90bc4c2af34ceafe88a3af6ac376fec35f1240f08af4f3eb30bc53dd68e5762e6d39e6b16f63003fbe0bee828d0d7adc58c41e857c2c44702215b202701fc696eae021af19c79e59c3e32627cd571f5db99b17f1772b5d746196befabb0b7446687827f3315b391d5dff069b1c39c00bb143218ef458e3b397e1c99640d57fc8db2e0083d3d22ed4111a8fc9e0e6f55fe6a56e946dbee43909bdd7d516fdf756ed8099ba80b1e17a5e279119345104379a36962ca9c8b2a53c414d79eb09fe79862ca749a9eabd9185ad1df57215945882f5894868a134bfc35c835e040e77ecf077d6a98a73ee022963d70b036be3fe5718280ae52c5d751211b22950c0597aaedd35af41f7dd5999e5f7ee34a37edcf97df54a46742b0252b196eaee454ff0c30685b15f8de087de208906be1d971f0fd89f7cdff2af0bdc96759d6889fba9ef092ad1c8deab0404562a7f3977d211c28dfd1573aebd5427a8773f03986101703fa19cd4ab96a381c76a747f63b63f7a9a3a08e251cdc593a024f63b443b76d17dd9e151809da3c582fbd334fa6dd0221b6d410c6a78ba95bb0154bb8999f619f2e084a6b9755ceee4ca3c7e0481a47776c8814f13054e627e37630d593bd09d5f10a049c66c9999f4b0b037e81ef70615d674c7c7975972994a053c069675fad3fae5ae3e779233b70254fb87f25d44c104afc3d5911b8b695173f9337130e39a02cf97356cb817f6cd23f55ef74dd06bd24ce5887a7001ef576262ffaa99f9bb5e3f55bda2aa0f199115909af48bb4d6b1a0a0847774515302cafebe75aad1f63362b1f38141e8721851c3ef1a247931b3b450581eb5d09027b9e3ba60ae9801d629b74991b7fd65520eac561d47115a85141d9a757bc75710bedff1630561ae05254ea541a7ff1846ed5e164834417556dd562c45543c88d8030bb56451fd5b3cbf10fb0164c5288789d2aac7e7a836e79bc3dd401a8e3e05aa6714ffb2dfddb3037c35fa1ebed62a073b2da42133f2620ae88de5e3f46cc69f2b9b3c9b88e39b8b108059ac6bd493be5f7a39f6b53ee825f4593b77ec9238f5ab804d533f48803e7d8187291ee25cfac4da5d8c9279517adfb09c422f6d704711726c73828a5082b4c7b3d85611b8f496d3e0f78c5c4f1dd1c722b1b11d55861f232beee6aaef8a00fd2eeeb45f182af191ca6de8eaa25ceda5451416fbf6d1abc0670b8c10e2815076f271044c690bdcb64856b91265bac202043a28f6bbeb807535aad4bd89e572a9427c826b170d3862f4cca70ddffb4769d6593a1cc6c42fd06cf68642835fe474a23e6f63df316f8361bab959b768d78e20c03c2a99913c162a9662bd9981eee55922f36792de0af68da04ab49dca72e3d9b0de79df828b433bcf6be073f851a36418c03a717d54d48c1014ccb793577c8393b7cb53cad6bc7060a54cc6363734f6ad388763519ca09b533078d3cfa61d7bdd4c4dd0ffe64d68d501b55903d3f4a1f310a3826ac2ca700de01d656188dcf577fd1b63e305614b8d13471f6f84a5d4b12c5e119870a63d1e3dbd39d3b5c26b09f9d80f8a59ce836b20bc933496923d278a022c00f3aac204d07d2e5075bbcef1e4820d633a3a2b35974f72a033484a91a1d6a9913239c93e5783b01833073c98f358e3465efd5087af37ad60b7285550e776d67ea7019e788776c5a456102358c32eb4e7c28096af88b9a20d8ce379ba3928a10ffd539c106f4927e7ac0f382c74017d6e4438fb128c660affd45e9bb68452de72b574eeffe3ce239d0718908c3800bc7e8ecd2fc7d9754171506017fd7868594c9373a96579fed475a28811649ce5dc8a3107bd0d8578748878ce4998684620931dc3981a2499568c2f61174c3b3fc46a7010468e8ff75c08cd43ac764d95e2ad1659f9db62e9554f811e0f43bb74779d923c8c243d12a5314d3c0c6ec84fe60e1d2b2e2b20d3e64054d62049ef9233ff55223a319c285e4e3f4c98dc95b2ca81230d7fed9bb99fd7d97430eb32c9c11647992bd85dcb47cfd58ea3e221d095bcf9374a6baa7c8333581f62b9e489282483023fdd18451f09bec764146b587209160b3d1d7a3d2e145fdb640c4bc382541e0d84255122d51a710887ebe1ccf29d41b4dd7fd7368d68ada250d3968d6f0971f0849c13c09abadb9db8b08960a18f84f0346ea0aa71227afa55b90cabc062d549b616400d36450b19adb67d7358e48c043fa1135abfca89374c906f8d1a6a845debf6b37f055d390b029c7f4524958bdf8d7e2755dde3b957f0926f9d3b8821ba96044d3cad2d637b973bfb657fcc06ff44c17965acf572ab7a0c87604c7dd1cfd136a0ad02b22e8ef320e101ea09772588e8c5b4d88f40fe1be18d27146a2b9559491949671700cebff9a709f297c2621ca9d5d1749623abc20a326ff5be55cb9435c03bf49b147b1e0a4a918bfdc3642df90b396a474f81d75c953d87b3f3b4e31fced630bd7c481c63acbb84dd31249101ac5277a36dcdfc80d8d9a2e928e9b2d65bb257bce97ccda83b187da8a7886dc96eab93d0864d88c358105f9cfe1ad0f0a8508b5b3985ff95de652e684da970b57669aa3fdfbe590a631522abe8246393639709a9a6cd549e78e3c2d1acf84643e9f554c5e076f75a5c1dce1be20a66722d0b896837b7036509ab8d473d5d2b7a8374d6a575f69d54afe3e7e18f4faf4e917be8a74e55c271b96d966e0c0b883f84b3ef2e4f278daeda2efd3ce770801d2c4bda5eb9b646deeab9fa55324e917e63e4eb6aeb4176cb4e43af3db61aea1546fbf16e76a12fcdbe726b565710e3f9866551023e5fbac0038678717e6ab4d3e92dcc53049e8cb65c00216d31a8869ff4d3539313fe2fd7ce0f53b255e3659e7dfc5f92b7627dd9ba42972f0ba72b888932d870ab97226040c4c0f4826be131fe1d2cdc21005ec2addd7796f0927501251ab26b0e5f3f9d2a1cb346a774e18bc233cc89aa69f5f70e3d5c17098eed350ec419c82837153b5c7f5813bf5918defc8df143063f3fe45125deded2b15892d5cebce589b60f2ada0f9d608983e8d107d8e6482b5f542c6650b014445e8c055aac142f16cfc59229fc9626f7aaa40cefacef777e494e13dfa93d27c201788ca9f60e572af8d65ffb513473dade5fe494cbf7377bd1ed03db2571d65af3be4b0bf27c1f069797bfb67ef0bd8a88c6286af6712c106df9c418d88054e3b46c88296a2e63894d6bee0dda8833c373d6a1b27637e1510fea3eb2fb34ae27354571369653a282a8d19f2c34f9e5ec34555b4ed24327dc5d246df13736bd41021697104f80c85bd0ae920e9aeb4e628fb8aec269d55858df149af298b06d61250b043c8a14a15f0646d0aaa18109d031c449e66dd7336044dbdec912b1bb615fae2a3df480bd64cbed74be65c8f1acac247e80bbaeb6f9dab38c6addf4f3b094d5934ef5c9749053b9159e280034e601731a12d6688ff27ee3581ae289de424d16676fb750d2ccd5b3f964dd77bdefc15bb204e2350632822384cc194cf9130f1ee81bfc3887d3366ec0b48cbbe0fe674281ae7445f03791887873659825680448f162452cef57d783821a73047078a8cf94c416850092ac772ef0b2e48517ef101ee0681b5259aa27fd56edf3c01e6dba6298ccc91b09bb304b637eccf8c673b816e74bd7f8ceffa6b17ab03df7ee9ca4098d24d044015a07df782a309cb6761528272632a6e1323c4e18284b463dfcabed708e4fc95cef133865cdbec8bfdde100621c65a92762cc3141ff37b66dea8fa6e3aad61dcbf3b512467c4773d36e58989e12a636389c1678c191137a5f7f59668c8a527dddcdd0c3fbb14cf48b8f3ea306850a5eda76c57aad06312d7bbfc18969d7b611f512358a7bdf959cc2f41de1c408133ef02b1fb2cdf8efe9973c27536434e56fc1bb4880db7fe901087b53ef3c0de18aafa47c25f1cd62c362f2e5da41c2dbff0e13adaba26c1e0829f027dc0320442e851eaed9507b70ac17180725349f6ea7b59bf39c095a9d10790e87221c7c2d24b8bca184ee95a3ef7449aad6c1d905f688498ae7a0cd1b01f76dabc342fb2be0295ca1484bece3c9b8a1b91e53de2d2587f3607a7f348f5cbefaa7a6dcf61bbbcae9444e2d25a77b016cbd1508c8cd319e9812b43b0bbca52df155d418dacb6ab1360a9e605fb53c6e20588a10bef42d884989e836b2ff16fbcdd2c1704f75dc8c1ac2cc6aeb92726f5d46e4784c70e1e249c102be6da506e5e3c2cef6a8bc4a60dac7adf3cacca8679f8f792ddc27613e44a70fd849b7617e042da46d65a3e6cf425f59b83cbae5b6e911142abd13a0a8cdf06d041435ee20e2ca417e905d2dc49c15b863ae5920ff7f9380a86bb0c86b69a000c157cd35245bf71f9dfdefbd1760af90ec3e554ebc511aebf650633221ca9157226f613f41406872765f8d7b916ff3877266f017b8d840dca0697ec3dffce7912ea9eafb62cc2f2d0a112c9bc0727444b47b62766bddf5b5f26d391f653b6894b069069979d0cf8cc7fc4143626a8420bc0a3866db3860096cc128d620ceff059d1614487004adbdf6b0c4428ac8897dcf16e6b11a692a6b465a92b40010f3480b444d4d2e24b0af8467666905c2a6233bdd6502521b621d3cdd4a5e1f268d65bf6a1879608ffd3abf635c5f0948f3cec7e087485c72b00258ba69783cfe7d611bc41c27814ef5674185791dbe626e1f276cf2c399a4eb264f19c77ee95d94252f546528f629188318e9ede65a927aafd2f2af56ff32c0ef39862d2f92268bc9400afa8ddeff591f3ef99681263a33b873bd9e01a59c8b281da30875245cbffee5268563c7f6f20b9e22d998934131dd219624d3cef6df2f3d2d6401833f72c619d6f763837141dbf93179d0f01375581ebe227185166aa7988eb9fd453d510ca6616cc013d551d23a33a4241e85aac3201284344977d496d768f5d920c5670b1d8bb608efc1b99abd261afb0a4ebe191605cc5c2e20523a13b3b94dd1fb24a27009d9a5b6329336f3516a327642386ba64c8769da1324a8a3d1f304cf0700df2b3e38215a954523e1d40ae96d0046e2929a815bf70785e94bc9b89246ab6aded60d65170eeb49b0ee0a57ee2e57db92409105c25f2d0c1a17b5556d06511bd0991a426258372c7f2b402dd533a75aa175524eb5d6b9575300b81fdb2258bd74429add8aa477bd1182db57107d411d16147defc3582861c68f5ce82e0a0316edd5d0f3cf36825a2c79a33e376cce2e63274b3b41bcbdd755845ad9ed2a3bdacb6fa3fa9484b7b60edeb1d9ef84772e78e39adca14c9fa0bb3ad1f1c17fb9449270e9b4c97b5b320839947fc73853fc58304ee9c9e86f3775f5469554d5006eb7ce9d02d5f900c771806c275ee7022e2b55d111338dd93ad51d14008df4c13d8c03fd9bb3689607e5cbdd499c3a372b487af74cb140f6300cd2dc2acda07277ea3dab57ecf09",
+    "f1a8f2d6abf7c44fcdaa6dcb1f6e791164004b20b3b4c860f409c1483c7044b6fa445f7224606894e386ba08057a387b48920d4de203b1acc4dbe2b0b4cbdc3f7d7bbb097abbf81e01db09e120eab83def925a059cdb513efe6bc93f0579ebf75638df3c3d7f9eba3c36a169e9d88495c452888853640d93ee70f254f86e2d2d3fbb5e8883b36fbd2da105cf3a75cfe998068203186bb37f1d1ebead8ce1f9383b816f1da2fd0a9e01377b6ebfed4f05bec08b4ff9b90e385736fd13a3af7980c21b0dab58decea8e9545af5d0fb11bb51aeda2c8616960e8f6f84e6c2fc4f50d7e413afe030f75475509fbcf49cbe14445d267994fd3f38f41a1339f2895c0b2969a9bf9c59b85e629486c7bb5107c7a6b069793be7690f7a7c96c93b09a9d610594a156ab27a32d5557a5b1ec8920761cd2f559ad808dff3da64717ea5f10fba87b8ff2712ce322eb3c288939e0007f779a3920f45fdd533369f6f85a8cce21f91552fe03702ef81a926af0e402b418fbb25a6a3dad0ec18ec663126b3f48c341e2725abfeae865352d5ad275a9e3ca20393c64d118968023daac84bdc724a3c522d97a5878ed788cf8e44f80f8803d57584d8c8688cff24dd8c0e881b62d16ea30104d62007a4bec051da7fdc95d1df8556ebdf607383a0825ae503e24661ceb8ba773b793360c3f4ed3b761bd372570cb17e7c2030f07b0b45a7974e45ee6fcf5bd7ae9e9abde5421b42cff6af0c6eb7fc73f4deb67bb4e0b3dc9b4008da30c67071243cda649091a14b89bdacf2ae98dd230e932d9b277d6968c65e0006a8ff63f283f2cd9c21615dfd82e0b24af6ff559c97922a3d112ff0ef4af9d6583bec1f84d1aa8bbae705b9bcf458f5d93059b90fc2217ab27d0072a38aec3229d13266beb3015ac2389a06dec3120c6c04e540886091597919da293a4a8c0812d6cd336d5c5faeb64162ec0459e252d219bed78c4b6bb61c1213939bb3cca12a625ce5a45001d7408f6d40fa9466377caa43afe961b5c1602679220258fae72a8de2ac69c0dc97c90c270e306dbd8eb681ba9c092896b19a8d42665b94ff4d5b8b188f19f7c44abc8f88d4ad7b5df1cce3465de377072c70dd20dbd6779336f05ce328ad741d1e4606dce7065347df111c7d3282c8a3fa4a9458561c04d1056cd53ec5a8ddd6bd4434ac910c69cea0443fd09ee32d1256da44ab7896867a0c97fe4faa4a53b6db5cbfe3812a6667f04cd318f3da127a0dd46170cfbaadfcca863e0d4240ebec1cb2a5952881fe89804892d36dc5bd6484cc78db41bed868ed1b321a680a293bc29c420cffb5305d15fba05c76c2138b986f799b6a3d061658e498204c2b641f2f2ba73d633538eef6b5a01117951eedb7611742c120ff24261bea605e94d21e452ddb9ad27af08ed972b7d5e1eae010ec5d83e4505f6a2b7d9a0bb32a1fbba32a2a8c7823e736a69f516b781fb5354be4b0a67343c009a09b8f656c34ab895f9213531fdeee911d677d1cbc5e72c0fd1ad1f3b4b8bc735e14c3f75f1828ea28c90cda40e0cbdc40dec37031ff3d50305d5a8bba1d53d2f176895e53faa3067129a5c97505799967e55e4e9d87faf5920d71055009fd060ad06691b78583f63881b566d4a06b639c55796b23531ea79c6de24092c0e6fb4d3dc739f6d82ee3ee39f229de4c844aba36432d6119be0d2f02e5f72ef1d95fb2494522a7221e18e92cf22e00010ffd93b89fe60b6895a37fca91aa2fefa8debdae3147fe4f01a6adbfa0a59a5203516b2cc7de5faf821a2e72d43beafa30ac379791ad1e5da3286abecfc7a546b80191b7b892cdd01c25e95506471f5eb74568257439aea03300e80699909cc06db2fd607f3279651f7392f80bf4fc61d66f0dfed7b7db09744139d7374d3cdd18d153dede2a65f26130506acc51d5c721a7989485a145dac9565ef6d3cc938c5a51f31ccc88bb0739920ef8f0a01145f4ddccc74790a22a3099a4b57e31b3a01b4118c9e6c393c1304cc51ca1784db5633eb96ccdc88f8b732815b92c9072dbeb61a2cc1e6b2e7098d883e6174f5af7bd4f129389250926e041ba94d1ac543aab6525f151294060791fd26b668d09302c3482c78e5f3271c0150c437b4e78b1cff6f2b8660dc310965f2df14a1f2ad45cd2759433c4f3952402fefd79fff00dd309c3f09a58600223441c11693cdeeaf0a6100d38d612a759a8e01f753982803af30c7470f7bfd1ccf2c08aa0b187382d25868a9fdf729da10bb0aa0e1cd9c6e695eb2c80c6b6ce62737c3e655246edbce5b8f7ae21c473762db0969dc216a93d4db239f67dea74a1de21d50336793d1ae45e931d975bc706ea718a2ab10d66a59d9d23f76969d870ac279611246ed3aab0f79e11611b312624d78b88a9d1a49dc68d6968f7428c33f0a7a65675826422f7ac058101d2f85663de331345b3a25cf76b7c8fe0988a13278be9599b8e4708526b44a70bc31ac5c278ab739e3e6f0927b72507f34b0034e7fdf43364c466bb75b559e03d4d18c864714eb6061f83a6331b3f59dd62f39bfc2529d5cc68bb6ce63db1075105cbd7d7c4d4ab68c9e65a32092e34e76c3178382a965f49386bd4aae307128242a2ffe3022fd7dc1a824b330b9f032d55573c2f004a6905178a2479ba8a2d5b3140ed5f3e10d986265d8b4cf262295658f301b4d36281611d9c61624928da9abc51ff9a6eb481310511772fcb1c1786203d25295e4a319b9c6d65ccc966b4c5795e6e30b2b3ae8246c38b4a911d1904145de63dbd4470fac47f8ee3eeb3f58b5e665c26a316362382ccc6bf8db7699fa3334cb2ce61c746a7d3af24d8030df6759835f5890b7dd1de538cac1dfe843ad06eba2e887f08d9a49b39246fb26eff5cacc937d63c8d0136f7a8ed2af4cf473f3f0d9064f97fb4fe9938d631f7cea3c617c38771553eddd606ab80bf792f34b44111933796fe1fb8bb104223a4de9e16e17321ea7f8de3306e75a2bc79aa5e9c0ec8dde9b3dd1f2ae42a6a278410afa8fb62c16282f1e3dc1e2f8c28d4538a75b5da7645101253dd43aaa150b273f73e505d490490314606264c737bb344b616a80a4931825043a740ea4f75847e98cc99c6880d3085787903e54c63e90b60f03192234ab20cb41c70c6e82b00e0575a1bb0b0f435831c9ceb9dacd1fab8a7328eb3e28533d5bfbeace430e21758cac204631bf033752f947f78ac2bbd9423c2baf4dea22fcc65c96c332ece9abb20fed504643e82f3ba0fff213635910789a2fe1f2cedef68799fcf4a86d63ab0ccd395d6d4f393f7ee8905eb77df32d97592fb34ac86dcf20cbe5afbf9e9cff37bc34d75af046a09a1781cbf51ee2e0b0f40096d85413a30de974c4d1d16ec06c0fad00716c4e10f8dae46ef3cf27ccde74502b657d3dd26b5481d9787f5c6034083ff88807896da55fd2c951a28f15c8c9e6c86ab50c369e5ba4f6311de505c07c7b85573b5a539785820c672557cee4b58dcda948fb51c95674c23f1275b423ee5bf3a646df19bb5dfa22747857fb5c605669f334d116710bd9f1495e242bf47d6b607c1c9d9c706ee770808484ba552c978ef64daabb642a7caddf5a55facba474b8a63577ac817dc57e48ab072bc6a2cc5f5ae96edc45af41c896cecd8acfc36604db3b7fed9d2d17d429f94bd2542b194a3d3405f46c1021ecf6bb907fdfb4b53fe445d5adb18501aa772c9ba75619214384260306ab68a5ab59161b\nCiphertext = 66c03198b3422cf3fd8291080f6fb3ebd9ad863e41cdff169becde726946a342ffa0ee547a27bae28cc782d95a90b0a618f717e3beb577354bd91e00a7a57485588265ad2dd0ab946926fea7c754c42751ec7247ee84c17262c0ed092186ec57d6044f0ac9deb21da6714ec7452e441e687e138ff144ea95636286263685419afd35f002830765d810b6f60e8dee0e6879995e9272c798b067d5f99f49e460b86d67c641f48240b61a16dc7cc27b048e8b8e8e80016470ecd2fc4225e29bb127ab48dfe7e7d5a65542176dd7ad40c07ac8b92891d595bbd7afb63fb6f9e1c2aa2fc659aa101f9b6a5c346625acec86fccf17f0d45809f3b9ee81572e5627f1afeed4ba96c6d3ed7e9232358dec01a1231ae7b94ad4675239f3b456adccec439b3cdd45504c5475bbc77dfd242e5e9671d103ba71a4601a7322e0e295357f335fa8d5651d528dda66575d106308338993e615b1c5bd7e95bf3f755ff726b4ac6dd5a43ef061ac9783f8f2804c68f66486f5844969103a36278ee0d10798bf8a802d3fee3a31294bf00ee74f087749ab3325c027d42b55b197469a5312bdc5c9b316b20093154e66605941d58f4db8d46a815c06f209c1dce2363771b5a794dd8d17e93a2fa7b194c6a0b79793c06f002638e5e3052365221232cc4b30adf161cc6e7865cf02911e2ac9b0a75f000e7ef3aa4f3c7438433513da7246d421f208b179763651f18e22a793961e5976a74744696912f22915244fcfbefdc472baee0be1e591d6503f2d9511ee1eededd9f5547c95eb94de134d0c2186109935207a23b2b8420a5858d831ed78202be855cc6b98d6663c1c52e1a0022ed7ebe0eea6b107da4cf50c1c7fced9744a914a66d4604a081587ce4b7e0f96ed408b8a9a2964314b1334a123d5184889958e6467a6d16e7615e5364e09aab75994e2758345511113321a3436db79351c63a282095ec6b99b6d775a5c09ea3f3225716e39e14df260bdefb2ecfe9a65c73ab4b3712ec842e43ccdfb535e3685fa39b4912719e67bbe195e5f0fe6c3aaada2d81b669c4565921f6c183d708b50c3f7172ba841815e9351fe5fbfe2fb1fabeb7cec9bd1dcf2d6332372f1b972b5144aa7ed6c5a985132f9a54469097e2e981b9e75a7df48fa79d0736c6f8a201c7c7d0ac8ac6512a7089514bf58442dbae0529135a7f2455e0ee5716c6610bd7600b3159197bcb20ca055695a36597bf7d3b18ecd08031b4ce3a643951e231c7ad15481e32ed7a3edd2b379c8e96d3288d5b93b562972a04f1b7e0abcc5090cb8655422cf5e9dac0b49678138faec81c78f113255eaa6110e95406a7e7417a6e221a8ec7fb9d55643bd589ace2da70fcb41722e66e0efce932cd7a34218375b6dfa3df1747953b24a41f94e50b84bad4d130d5dab4194665338e06f102f46badc5dad7aa06edb01f8a31244dceebe5e2006d6ab4a31582ff46731b19071c08ad1db79ba018687f3e6afbe703b1de26c11bc8b62fd6b2fa3219fa7190379504820abc97ff6c034f7850e2c7fd335462725db6748fe45920c213c539356b691f22eb490faca24e99f0a044a9f727d0786566ad00635983692ef324bbf1f80c42b269e9d5a8df3249873c51521c81400c729ed7a5e73995928abe94d189cddf2774f1735bc2060bb2240e558699c365dee45fa68801e6a1745e03736ced1b89fc2755565e3b36c2102594d43c451122d94f4a263664bd26b2fb5bc7700319f6b08796864f92d0fdb41710910bbc13aa9cc7baac3b48a24e4f3573f315448c317c149ddb433d9ddd2a2f0cfc81c22d3dab31f184975355b41e4b36fd8f22e8efa01d61a5cbb0e4fcdd273cdf68ac73fee745faff44d44d93c5a111aefe4a5ca8e8e7c075ffdb738cc5b6466dff78ddd837c72c54941707b04d60bc126a3a2fae9540ec2e4672ae13de0d927a7bd363f8abb5a56364d6d564df90a46df9fd59e2c54d5bcb8280415257a6976d8fb24c33330af32600cd1559e0eb05d55b34be456d434bca",
+    "98252fa531486ce2a24c8bdea1d57d93a550ec586920903a39ca61cbfbce79b8f3a5b1653794872b2c614458177e748f8dfd43840e5bb0d608c26389347673fd0b005f60f52c56731ee5faec6c8d0617fb53d5f2415c2e7906ea0e6d0066354b213b3e94f4dfc311e4ec6afa7e8d1c69a63cccf8326741456a5e0bd0a359b7a37c117f7892969ad7b70cba9bea0a975ada7cf67e0d7255be8d2c6e7b8788b9ff14c5d1449d6173e07b5f9d94560d46f474ab2a67056fe9f4a9fd617a617d23143adb4e7ea35f2d5cc1398fb9ed43ddcd10f28debb27eb13533110005e6c78ca4a874db68c65081ecb8bff1b64eb1e2d7b76a1da3b375dce8a92d32a6277ed847879345717b9649f27e846a701549311c7e69a96d61df616157a114bdf1663ad93a26c28e1a62ee4a7c72bccb9785639eaf1e569decf777bb0548ad9ee36788cfa1150eee3ca3c96f09052ba2300cfb7526b9424b6f7418c27a1e9bc13e4d9868e5c330c051c3885e44714bddf7cb090fbd0f36b826aacbe191dc8c35c219e19fe736198c29dc4fa1a98b5fb1805dc29ecd02f74d4510a3928448b5ee61b5991e46644850a4885bb1ee272883faf27962430de1922d0883e7e80215cf5fe7e8f3fd0e2a49bd50727af793cb7e5b40860e80a1fbb9d5b5696bdf2f741909ab5a713de47716332df6c4f78288edcd6ea130d895fdb2f29f94635bbf2061de55f1801bd6a24294aa199d78021a1ba771c651de4bc08f032fe6ad7a5caf6a6afc6de649b901f783a0ee0fea9b803beeb0f431400d0707f159d7dc29c0c334a918fa08a653137a4a8bc86066c8800e1d171f1dbddf1fab8a3eff6b5023da96f002e7e217e826fa378b15dc8a376db30228f5d6b629f331a162d63e53e5b5bd7ff9ec098b4314285908281930ff0a8aa86a6d89411e6b5bc6b9c9e931623ccca6741fd6d36311e6a8e323a37ad40b7a2797b84694e736d9c135e52d149c760e727598726378cd674b0f4df1c361de0a12a2b8232e611d789bfbea699e8e77b99f3449609caff3d6ef7233df8cfc624376c905eea46c6f77c0b01d288868a19db77e227dbb5bfea5cc3f49d219c7477f7f2b3447b0b8efe08eab8f69579d727555e547c13ec7ae13b83386f2adf634140c311b6e2759cfb9c8aca1c32bb7c002d0f46ecc526916589a29e328ded9679c2163838f071b5b85b35e5e7d99c3c45d25bb9d37d7bafb8350ad4695a6e0cb7ea7d93868c30bb54e301e21147696b7dda156226a5ef8c62121e6b2cad0c4e192116192012468eaad46bea69a140aa3cb9056dec87c911636a1e55695b9e5a27c63cd8c03f31570d4b7507d13731ea31f082b33c6db8dd6e22282f9790be41350a96abfc4dc3de78e0a698930f540dbda3fee923a463a4c4a66bf00bb2cdd6d22b62a47af96b78b1f0f0a174e4ec5b785b3820f47d3c8cc1691d4751ce4e4ab78a4551956158a36717dc35488e890d0631241906db565603205e054815aaaaf17945c3372dfc7193369871e2e88fb84c15a2b9071101e1208177fc18397e6af17b5843e1fa75392d8d3ed214975d50f2b19c24e83f010f8c394ec1edbb1cb912e61627d2760b0e630b986bba2ae113b8f3b51ba00ddc495520274a85e6f6fa7573ac4ec6e2a86a1da9199ceb007aa6f132e5ab8ab8fdca7c829f452ff17524fec475b8f485b29fc6f0d972eea4ce98e242b5d58f6ddc1b3a71256de1c584c9914a3cf1e469f0033165d934fae68a7559011dac7a4e0c72e3b398fab8f8cc2fb67963b0f9220f410e5ba13026a27288a1d49edfaa51e8f220503fb5ec476147cbea975994fffde3ddc51bb189c470078978d238f5287fb2629d23989875d74b006a4122f6a342c996d4a244e8c5e4b804a44c301ac4d6054181a07964b279e0a44c158364395a2ead40053d2f3350ea0529a57552ed835513f533ee0c4b94ef674f31851616a4fa2d0302d13cd4aabf5f96ce28219c0b5bc0e5410fe0fa387ba1009a6f2280f9e7bbe20c33be5eb411a5f6327714b3443b4152cbc54c4012473237dd98b0490fc4228ded74afc81be2a58a22e03ca987faef5310e474f4f5a183f6b7ebede5a8df8a0f94a87a41852826b29466fd761f40b416ad0f263dd34e5497867766a361af1654c3fcd6ee7e6bb3f72d64cc980f04305b63bd574f116d1aa35b4bd642cab0cde6a29139aaa163805c6c40384313d4ec6027c891023083988c1b0d2edbdd9b1afe102fbda285a6f897efff72a0d7fc19a3cb6756cfaa2371e13be3cd167cddb90d525cba7da69608b9995cef92a6424a14df6b860ef0f09830fd7189497a432347680de0f463c0aff82df8098cc4f7753f7680c8c7374d01046b05c63be73f3a1623be778fdb0bdb90d4fb4b458af2890d15f108b0927304c91c8d62cb148c35cc93797db3ef9bba1014d89859a91da0c0a971f330600d71565d30e9c9ea8c07e7f629e1a6d578da04d37e597261cae8ab7d9a952bbf71573f1bf70e064f36c032cc624e3c980e5ea46d36232d61a57fa598347b7fb6b28401e34628b051d6ca3dea190d1d3c343fcc83175f70f77a8fc5e8791b9788989df1e37cc4881648f4fc673772003079adae55c83cf02a894b98561e4a6e4416bea3df18d6f702ad5c4f40faedec6b53cfdb5b3a52d7d43b97ee23ccfa2d30c7264ec555b15f1d9e7e19cd9890a7e8e01ff21d3b8b451e50932f189a420d18e7c7e2f103332c78c84600e5e8fdedd84f055a8b39be9a52782d47c6205c0de41644b09c0931f2da269a7e58e669f3b61ebda28ab8e3f9b83ff3d2bce37864af494860b2f01b000abeb737fbeaf8f9fa6378366606dcd0fc33031b94f9a7a0e562c08ea720a671ff92520047f69b138b4e032c3828874ec4c29e49aab302089956566372b20c0216b601c3958ed9691bbd89f1df45c6613d469e3b9758a70c860fddf768b10a6bf70237a454a2c0b70dd5d02da612a91fc5731513012a4a6fbc16d01550bdfdccaeca22bba104ccf6aeb19f21d4cdd3da231af8ec5bf2a726ee9cc7c85b8ed46d2f6fa4f1b010b2561fb69690d5a9df76d729450a6e139962bdaa2bec0254c5a252b97e7ce7eab1817f454c6121130952b8c40628065dc9b77b0f953552f5aa3ff983b6a51a51dd87c2b51a18e14adb8c80e002d0b47c61cb357babbbe3ed51d371941a8f111837ecf0e45020cb941de170c4a1b5e61bb928b1b11a8d902febd2ba016771f171b8a7ae825fcc4642d95649d53675d0027822e4ff79ffd302bfab1a0ff26f3648c7ab00c10f8d95f21e40ca2b40691bd4be79bb9ccc0bf760a05be4728bbc0a64e585207d1d09393a80d5f574442d6a933966777ab05f699c4e84aabbf753059287e7261d972745906a4fd8967bfc80ae9b6ec2ee1b22a81775f4f24999987365ae2dfb6739902ed51b9a4394fdf29f216c34567102d9db301661b09b728a79e377cf4bdfcf5c83b110a2e267abf6d40947e643ae2ff0c244af168c9f33e7685474ac30611ef95f218e0dd280899a92a41e7a759d03ce3709c2a140ebd35e199f1dbb96f7351cbe1f3de8da8c49758a49b9e724ebd3220ed6f51112944f70c0d1e9178f68a2c9476a913de00abbd1f5bcffa646f926da77a9e9fbdf81cdeaf7f9b13e843afefbca81c93614f8f1675325965b5836b8a77620a5ff162e25366718d8da7781e1a7e01fe2e9e56cf958c6273473abf5c2c8c7fb209307544e1c0726d5571e521621b18b6da3064b473423536b1b76ed75b21b4ee205d7ab5f081bada63062706bd155672dccf84614210d72660095437c6bc2213d9c904a4ba1bfda14d350fa3dce7141e817a50859b1a74aa64560b2ebc67add9f945b6e85577589817078c8ae54a9fc311593d2cbdb6692b089ee6264cebcc7719753f80e30dbe48b64fcfd1037fb9ddab69a5ff9e5898bd8aa947d9ad827c26df67c6786edcacb3478a20bded1ad8c48018ae0d439bb5afad5d39bb8fbaf22d72ffd759c4fa2e94a5a89f41358ebdc4c3aea5110f1965a049fdadff9cf703eabe9628e2680fa4e70320d304ecaed13f513f27220db1916ca1500f1c2e091671fb71329dec0bd6e310c83e67af61b8ab60ee1a8d559a508d174648b1bca451ef0ab0ee2ef74f4fcfaad1cc5ea6cadb8f1bffcb1f2c05122011ebbf6abc16838e452fc47653821589da4cb5bbac10deeea3ba0e0a6241338e64cc78d7a923d018e8b5b51c4442070e5b0e6f1e8c2b83791e930899c5897a602c401c1b85827962ff56d19c06f5af033059bc7fb1bd29b65f66aa5b4397834e846935e523b16438a42c1f990ebe4f83182163ca5fc60a4c6d77fc182e81fcda943a962e9e7f00f6399728b48bbe38d8178fae3582c8d9998e49df5f28e32d541636df3cdc8ac00df45db12da2e5e76f366c1ea8667ba5f3542d21f58ead7c55d06a4b35251b8f77dd34d3de262947379107a06d2f4891ffa0ad3a3e5bb2bbbb978af4953310d4cbe5525ab344ebb98ed24d003600de8f3af36ff3d0a7efeada963845d573685bec2221403b994f97b1e714fd7dccc300b62c2a516e9c6780983062eddde0178e93fcbb2ed4f06f60767356a11d22ca37078fda1ddb3cb907d1020f62ba85d09044574ba28aa3df36988eb8a41e4305e5b0687abe43a90e4f68f0374b6b05049aff5b065d7688cbbfb0e96ab03df38903bfa1c269f43a114085eb4596aec87ced88701b42f0b7426389727308bf10aee9d8f15ebdc411ce1e764a290a12faa2d7c1126dc7b5076f219b826ac8d380b69af7f95d69fc3929a97f5c7da1db6270e9ee1f2a5f7fa3a1b6bfcca00463655121f681d3a627d03efdf0b5fd045fb153bc4488a9a8b7264373c710ebfdb1c267fdca37723b21d5c3eaef48e784bd76e27c133cbc24d114f610c79f2a1f2c30d87ddba395887030b65097ca5566eb0361e70615b46d4b86c2759f1cc2efa3915b4cebdf51a745fb3c6cec69a1fda2ec5e884dce228e30af362815d2d8b59a14f89606bc77439042109369a9648db7d71024ed6df06c8ebd22e8623f48feea77f48b5e88827fafa84b0564151a5997b7f29c4d3d18068e34f2690a293d54003d0ea8f3bab9387ca72212cedb5f4602ad047dbffae2ab3a4cd2865bf896cd96f78b90e4017eb7e3c7092320c0a37f81dd65a5c4817a4e7053e6d2bcb23b11e09f681587f3a9361e974ad54b88c72c296629b1ab754d25be15e87c414cff975fafb3d7cb68167b21f1889685a48966705222b525fa47143b00041df94817c275d93c2550fdd82471cb3cc1b5644338060b767e807bca902c180b3e535c77be2651b3962287b6d1f6403033de4e0aa3a20615ab59d290f4b167325959c1524ef216dda2ffce86b50cb6b56b62a20a043d9d78c704479c22340151df5a1907670f8d4f8c90d93f7b5d94d04a4d383914867aa3c0e5ac85fc299a4d2801a3f80f4b0f046fb62c1c8c539a83b21c7549df0afe200537b52c80ebdbad8a438e430cf876cbbfee9ceb1bc5270577c27d53b40ac153cab377a565b1a9fbdee8bf8e94839c0fc04f7f664383bc90d56ccd1cc01b465c250b158b5e6f321c20db245602d10aab80c553d52f17282b095b5e2234c6c689a84b096112100359816cef7e92029fdfc048058f847cd2f2369ceec9fd171a0487bd7acfed6b0319832df6d59affbfd460ce8d12e4171da0f094e872a2888fe74925c5ef0621c4edad337f7006086748913b24d4d48ce36e662fefbe672b6d476456b1fbac6d80030ab93da93acb4a7e10f955",
+    "547e7e20a0abcdbf909f05a2ee2e0b7485fa16be652b9d9fbfbf01f082488a81022bdb69af9e6fbe753e9eb92a1762afbb4df49f83ffc0cf03db563aa96fc5ba1af6d4d7eede6067749e8ecec79b63e09742e29e99e1c960dfb0688b0222c49ed919379ac66e3fa1c72645122d1664721e78fefdd1224c0b886f6e214e37d268ca9acab76ab3adc9f5549e5dcdbb3d31ac34ac472894d004eed71f88ca2377fcfa48d3ae43805dc612891dadd06c263ed8617194f890bcbb964f010d277ddce1f6682e661577ecd51a4d5421f00935a5b24fef0ea1809fa5c4fe9cf8c453046f61136ec8872915d2462157d73a205d56d77bb83cf16b88cadf6430c0e5397fae1f91a6a11b177bf04b065a2e55df81d5c086ec8dc8a0a660eed37d41fe4d8b3e3f22238e2a63b6e4feee1fe9a140ed37b2be4193f75c2d038aac7f6b7dad2a3b37e5b9b660615ec1db77a9b7ab416f43e66c872b71cb67c9245c757dc87723ab3b9544fdd8a16c9486e8ec3c4a44cefd98535d6e5683426c1cc8c888b8e0c2e7528bd7eb89b80d9e00969efd2f0a0fb09845426edf0d1d9a0809648e7e46ea0a8c9988bf9df475be12a72c7326c1f2bf01afafb190cf6f649133c7dc14ecf9b8c971135bd303c8894bac637e08257d45e1b68edf550d896c41682c002396e8f1eb7c1e2f4e0ed9b8b7010fc7847e6fb1c5907c17b2d2b7cd24c96f47406bd04cfcb2099d82dc2902d6f91e2f8f3a05bc62019af536309e7847fc06c10dbf7272a1509079fd16bb16a85ae2e078f97f9ce66bba66d6329c7ee70f9688f6d91aa38b25c7f4884658a72ad8cbf96d7d7a9652673273ee1b3d4d17780dfe9ca865416e318bdcbe9efd8e071fcb15ceb0743df5af4f7d598b31e38677e65af61c1109fdbb11fb11e3952e6c3ae8abc3f894ccdf205ae55dafce1dd05dca6b899877f57d712223dde4e7fdec7e0ed4f0a29ad359e318eb36ddb42fb205adca400f5b2615947c4f0ede95788093a1152d88acbbbb272750823151e245354e658452a95f21fef05bbfd98a10c1c975ad1a08c59fa3efa9fc73588407a83d0b26a53f1b4115f83780bc70ee2619d7374ca45b9e200055df1b93977e17aca89a009110a6e74caec7f86114f91975bc6e8bcdc7267ed2920cf12cd7137840628e1b8a0ea181dfef18dc5f74e752f842ea91bdce4b420ee709bca72c4514e92bcff55902e5529d77fd95f5837c8f4fffce80c813630550a0dde24092a25f65eba90790a06f4d4c3e739aaa8194a147fb32e81c71d3e8def79251c33637661b0a621a2a6b302dea00d34a9dbe9b621c1dabd0464e85241aa6712d90b4287cb23c17bf1e4d0e6dbed372e6b49c4a843305b3b0e5cab0b0964a93ad0bbc99ee711afa7f2d0a296a375fdb3176c65a957ddd9b88e9d57df736acdeb02a71b924cc2e972f51ba68a597215678573bede9ca5b3a0a2461b2d3b9ea57a5af8c91d40779bf917ded32f14a66d96e28e1415fea1e9306654c6b84d8a64243a5271c1f11590423c718961aecf5f659b49f67efa78e02ef2524d0966ebcc446d73d49ab7ec31f0c009069d14ccd63f926169291b83a3e37610054b0b964741e2ed8771d20bfa225eac0280b4d5af0c09d3218bd497a035536f5af0816884d606f1a872b8161a266466b56e0be8b80a7bde65ac706eea8cacf1749e5e71ff9fa3e69ce878427a0728d44e666eff977026abfe18cf3ad156a943b917e72ad65725a9a8d60b7b5740494fa63143a7f2a94fe6d8b319be55d6fe1a988244deb798f345f30dcafdb6af9e9cee9e35733274bdf3896750897371563ed2516c4ca6c3c3c994b48cc94b67e8129d234a0e19dabe39e500214c0ed5f0e5d61b2f58d7355d147102d93b2689bc5185dd4c0a18efd11a307b887d4d0fa84fd992731b3a80dbd027dd36cd6933766c537e8e9e27d35d5187e8276b0f59fbe7b6d629d3416b782e7981d85e1e890853c3aa94a93c1667a55044ae42badefab979fe7d525c6a180307c5ee3a9c3933038028c3e1d15d1e78fbf53b6ea61ac5e02db0161719398a31570c55f73cb47ddec8f99e3e14af5adb8d5cd179f4204d080331e75bd391b19d38eb81f148c36af3e8a3ebe76209bb75c9741a89b5d0708bb0fbb0945fc6fcd6ce142d19faf0947c338dbc8d976963281866b5216421c00cbd77c0907d1e16f5e925319cf6c62f8c6e8eff0c2f831c504e7a1c0df09a54e2af708ceef39ed7d0f63d83429e9b0920c03cf85c2244f2fbac3958847113bed577dbde8992cd91be5833c75faedd5e2005d4f7b66fab8fa9305927406f863d1795dfe04028940b765bd79de6972dc7094fe1c2503a73d7b50208835216c23aab3e47094587549fdd74bb50ae21cd1354daab632fd0907e63f4c2b2d39d7fdc4fc216bfa742b4608238623cb7fa01bd851c1e7ad5ef5215173a71f363fbb7dae8092486f4a1549e32ae53b14c1343ff7fb5e2b1487d9c594a1b56e22625d275e41535534d225b7b2c9deeb0d30dba7188cf75d680d4545ed05044a0661c690a37fa14a73ba8c68357e2c948e290b5d9a4b51822824614ef2938d19ea4b650041f59f3b548f0a305b86f55e69760f37f09dfdad62651aa5fd84eef28a4431136b34a49c9bf1f2891364f86b0aae70b0414e821e3db1533b0f1db5fd232308bf118f858aab5ae974c10583f61b283a3870eb82aaa8ea3c4e2ee3c3a3d7169aa8e975ddee7f620f6c5bcf3eaaef0101b62cd54495cb8809052c9e3151690cff7c1efcc4f63b22472111a7c5d9d7d2a2be951510f60dec8c426f14700c8630f8a14dfd359addf5d9b7ae031a745ecb4e17321b385799c90f924c4780287ac187530a40b064064b9036cc46e3f87c4d23aeeed1bc22a5411c7c503594d5d1261eb9fc4da242493beee9f671485a978a32e965faf9b0e2c13f78e31e1630b72d35b4be691e90b3798e18223c1b514b39a8e1eeb7897c22fdee1e33fc76e2b2f9298ad4fd89f44163aaab23d754d98c7890e58708b81b3832aee31aeca85e76416133710aeba0e5d9f17695e607d09ae3f94be191553bc39c6df03cefb4ee05516fc02d66c9866e4eb0d89a662e309379a347159db2e070abceee226f2b8b62847ef7c51d69c5f12eb567fa13af4b4f90b3f3d9d4b6a3f68bc4dd77075081e2e99833c18b154d0d6ac360141de2a25af61d551f10a34e03e1419a37409b4c177c51a8d248157b411868eb607c34d2daaa453a0954fade5eac45d5f21f50efba8bbc9c87ff0435c70f064b42cb2d158384fe0a4d9c90030ace7723af0a6c8faecd8f97f9850e2a489a94ebcc655301e2e14711de9eb08726638a9ddb57160c5545c152a26860a17dd18172bfac138a300f60431fc49eff18c93f71400e887f878f4dd637cf5df8c1e2b12c0f87e31ba2754ac1748479eda0c4184b528554106128320dcce349939e5e6cd3434f86dc7adfee28c008a21ddf9d0dbc87ceb14cc3afbef1e06fb3f9908a4b14f5e6c43b23ba783b75a6cbfa2ebac6533661b8c1143a34e8e2a9723389c4b7087dc07701c53b169894551084aedbb423bcce2f470881fdc7240c26b3b76fd6cfeebf8eb2828b4741e5e8698b19fa0a44703cb4e4c8ed6a7e4d6063f5fab724e08a159f4f04a2f351dcfb6335ae6697dbeca25c76b55e6ec9045eaaa8706902df492b8c8cfbf68c4cc1be5d1e5a173262e38bde051656ea85ffe35d97f1b25f6a47381bc327a946f7cbf6210adfd957b2921\nAAD = 85ddde4720659e80e25168585a354eb1e021c0b5d2ee289f2314dd5aae52bdf1fd44755bb56a6e659111a1d4b4da73315bde01c7d2c15a4f7114aefd68c141049fac27acfdca24e65c51fb1c27d307cd948e13af2963166bbc9411401d124f1ddf20f890db5611385257f52aa05c09b467e3ae886decf5744ec3749e5879f2a60017f601bbee11a66604d5f3d521d2c48cea1794f77366f29c7bd12a8aa51d34a4f3fb52809561b527016bc6badf9d136156c330e1d69d1aab98c7caa9cb46e782a898b4c66e4ee3e2445fbfacaadf9a8f73c4cbcb2a1ceb604ba5637b51337fcbe0fc366da98e805ceeb29feaf05420113b16e1005079c0e88af33f5970b3d7a8b51d0d9f5120a0795063db508171b75ed07705ac6d6bfe4ecc59243091d48865536515e036860affa880bfc91aae2fd1700de15994792aefc4a176e5d49d0f9135c7d670f3cb8798bfbe83fe73de7427e0f3e6a2df561cfa15ffe6ae80d5016096c8875b0beac8cee8fb530fb421b9a8ada4d551a528d0a0b521086f5a2db371a3bf12a2ef861f831fcb44cb2baede907a9306d3e5a3af796e0a50ba2c8dd61fb03727df5f0654d837dabee2fd90eecb7b2e8f303b0d57f97dc6a52d8281574d8457c89c6a9f5d80e0bd86c90ed39b1db4253affee614e8cf1ff05166c66e7d2a2aa2fe8a81c4741339683debe189c126e7f553a5f2dc16fc16672f74aebf94c7e3041c758fbc6d0c7f71c192cfd0fb2ec52d0a0705b05815d567f3d19f9b5d553a2adce9a79159b0e38980851bf64e97f896c028a6df8363cf1f13f4654265a7b0c0b24198efcf4418c32772bafd3980dbc689fab12e85b3ef4a491e2e5ffaa2fadaaf3deb392105a42380797d3b41ef61303a6016b269ec9a9f6e3f26070ff33cb467435ecb325dc7e18728a5c2e882e720c8f876fef10f5bffd5a925cdc9689d934272019e90e3a3bbf63a295f207faa5c014e1517c7d5c18c3ed70e92304d51944dcd3604c999d4aa8d8dbf2a4c69cbbc08635c968a20dcb80f438d43c57851c4cafec0b9568dd6c19932fd3f1294afd16f019f20e40ec87f6f5dffc7717470614b2de6e9000969e6b7e561cf91c06dd379a09c6c25c7841330dc78fc5be1d9b86581a81f55c0289531128638441fc98a1ad9472d74e2be2f874aff2fcf9c941502f59f716185a4c39289ca368c6dbf5257b5dc5e57a420792c26e602e4ecbc4f17c8787004eb88ea091d6b6ddc3c85dc110b5d1f46f6e1d872723176f4c73664ecb4219258fedce19ae22360354fa4894fe51d69434c2e58e1ec665b5cc33bb295053c591b474b6ae178c8834667bef971604279440170ebf3e739a4ff19704e5886767f81edce95a3dd93d1147995e7eb6c794b7be136658ed23cec7c374705ec0d8479dfb44cc7213076668e5fbe6a508537a9157815c6e5187b89f\nTag = 469e3ef168a64945f76d7a2013f27b68\n\nCipher = AES-256-GCM\nKey = 0000000000000000000000000000000000000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext =\nCiphertext =\nAAD =\nTag = 530f8afbc74536b9a963b4f1c4cb738b\n\nCipher = AES-256-GCM\nKey = 0000000000000000000000000000000000000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 00000000000000000000000000000000\nCiphertext = cea7403d4d606b6e074ec5d3baf39d18\nAAD =\nTag = d0d1c8a799996bf0265b98b5d48ab919\n\nCipher = AES-256-GCM\nKey = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbaddecaf888\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255\nCiphertext = 522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c",
+    "0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad\nAAD =\nTag = b094dac5d93471bdec1a502270e3cc6c\n\nCipher = AES-256-GCM\nKey = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbaddecaf888\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 76fc6ece0f4e1768cddf8853bb2d551b\n\nCipher = AES-256-GCM\nKey = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308\nIV = cafebabefacedbad\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = 3a337dbf46a792c45e454913fe2ea8f2\n\nCipher = AES-256-GCM\nKey = feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308\nIV = 9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b\nPlaintext = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39\nCiphertext = 5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f\nAAD = feedfacedeadbeeffeedfacedeadbeefabaddad2\nTag = a44a8266ee1c8eb0c8b5d4cf5ae9f19a\n\n# local add-ons, primarily streaming ghash tests\n# 128 bytes aad\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext =\nCiphertext =\nAAD = d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad\nTag = 5fea793a2d6f974d37e68e0cb8ff9492\n\n# 48 bytes plaintext\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0\nAAD =\nTag = 9dd0a376b08e40eb00c35f29f9ea61a4\n\n# 80 bytes plaintext\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0c94da219118e297d7b7ebcbcc9c388f28ade7d85a8ee35616f7124a9d5270291\nAAD =\nTag = 98885a3a22bd4742fe7b72172193b163\n\n# 128 bytes plaintext\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = 000000000000000000000000\nPlaintext = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 0388dace60b6a392f328c2b971b2fe78f795aaab494b5923f7fd89ff948bc1e0200211214e7394da2089b6acd093abe0c94da219118e297d7b7ebcbcc9c388f28ade7d85a8ee35616f7124a9d527029195b84d1b96c690ff2f2de30bf2ec89e00253786e126504f0dab90c48a30321de3345e6b0461e7c9e6c6b7afedde83f40\nAAD =\nTag = cac45f60e31efd3b5a43b98a22ce1aa1\n\n# 192 bytes plaintext, iv is chosen so that initial counter LSB is 0xFF\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nPlaintext = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 56b3373ca9ef6e4a2b64fe1e9a17b61425f10d47a75a5fce13efc6bc784af24f4141bdd48cf7c770887afd573cca5418a9aeffcd7c5ceddfc6a78397b9a85b499da558257267caab2ad0b23ca476a53cb17fb41c4b8b475cb4f3f7165094c229c9e8c4dc0a2a5ff1903e501511221376a1cdb8364c5061a20cae74bc4acd76ceb0abc9fd3217ef9f8c90be402ddf6d8697f4f880dff15bfb7a6b28241ec8fe183c2d59e3f9dfff653c7126f0acb9e64211f42bae12af462b1070bef1ab5e3606\nAAD =\nTag = 566f8ef683078bfdeeffa869d751a017\n\n# 288 bytes plaintext, iv is chosen so that initial counter LSB is 0xFF\nCipher = AES-128-GCM\nKey = 00000000000000000000000000000000\nIV = ffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nPlaintext = 000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\nCiphertext = 56b3373ca9ef6e4a2b64fe1e9a17b61425f10d47a75a5fce13efc6bc784af24f4141bdd48cf7c770887afd573cca5418a9aeffcd7c5ceddfc6a78397b9a85b499da558257267caab2ad0b23ca476a53cb17fb41c4b8b475cb4f3f7165094c229c9e8c4dc0a2a5ff1903e501511221376a1cdb8364c5061a20cae74bc4acd76ceb0abc9fd3217ef9f8c90be402ddf6d8697f4f880dff15bfb7a6b28241ec8fe183c2d59e3f9dfff653c7126f0acb9e64211f42bae12af462b1070bef1ab5e3606872ca10dee15b3249b1a1b958f23134c4bccb7d03200bce420a2f8eb66dcf3644d1423c1b5699003c13ecef4bf38a3b60eedc34033bac1902783dc6d89e2e774188a439c7ebcc0672dbda4ddcfb2794613b0be41315ef778708a70ee7d75165c\nAAD =\nTag = 8b307f6b33286d0ab026a9ed3fe1e85f\n\n# 80 bytes plaintext, submitted by Intel\nCipher = AES-128-GCM\nKey = 843ffcf5d2b72694d19ed01d01249412\nIV = dbcca32ebf9b804617c3aa9e\nPlaintext = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f\nCiphertext = 6268c6fa2a80b2d137467f092f657ac04d89be2beaa623d61b5a868c8f03ff95d3dcee23ad2f1ab3a6c80eaf4b140eb05de3457f0fbc111a6b43d0763aa422a3013cf1dc37fe417d1fbfc449b75d4cc5\nAAD = 00000000000000000000000000000000101112131415161718191a1b1c1d1e1f\nTag = 3b629ccfbc1119b7319e1dce2cd6fd6d\n\nCipher = AES-128-GCM\nKey = 31323334353637383930313233343536\nIV = 31323334353637383930313233343536\nPlaintext = 48656c6c6f2c20576f726c64\nCiphertext = cec189d0e8419b90fb16d555\nTag = 32893832a8d609224d77c2e56a922282\nAAD =\n\nCipher = AES-256-GCM\nKey = 53a6f0d9b8a81818f2fd7525acd65acbaac82684cda4fd357b1ceb6146b31ee4\nIV = 05d8a92b5a510c3a3dacbbc0\nPlaintext = ac0ae17d3d0ee5935e18675c36d9e43967f6da38dddec14c7ec574ff8473e11ae5019e638232323c175b7672a7462df6709f5014bbe12a1370a1ffb570177927106f995dc8f35bd6e6228de7c16acb71e583c87477dcc7b17a908ce01543496c2cab8a14a21c43b18fab52d8a882dd1d999b4275db34c7f32bcba624d128580d7566a2da4bcfcc4136d58816c437d21e90456fc86381b946b8955f0448e83564165a629cb2edb978e5941010ee9153b054ee429b315058334ad7899aacedbc0bf423de69f57c633b56033c6531dde29258694045c46a797987471ae6af8fee8ad0c1be4149605064aaebafd1c5592e61beca9b5c7771410a276c3ae517490735ddd6af499ff705b9fa68d50650e60c19f5ae2c88dbb6d612afc7be28a5f55556a2163b6f66609f7d9ba7e97c074ea39a618727421fbfbb6453ffeefa643decf11404764515d28fce8ba66b8c85d077c47a54125a38bcb6b0adf6d248ba0a9ea129c887c66ef537c45e9fd3c17ce352e3936cf139e13a5946a7dc9dcb6423ca6a051bf560cfc572ef366940e71c81aa302cb9701f9a5206e9eacfe9835bdacb6425d058022a27fe73e5edeeba98c7a3edb761578ab2ad5a442c2dc1cb3c143c6f18dbe525fedd2a9cee0ada3b2c116465c5cca9a7e5d4374b29",
+    "aa4ad8adaff8d6b0d1ac3990685240ce022faaa07241f9ff445566b9e0463350792cadcafd5fdf5c37706c0025b3c627185b356d39dcb2244b15566e6e3f8942f730fd6d855daa1456fe294f9156c4b5131e5bde7f2d938ceb6c7f5deb0f847a98b7fd11a3f5d0163eef9bbeb83cfc96dd8eedd447901ff4d3a35c0ea1f691b01385eb39fd265f756bbd77bb61b1741db0502947b4b985382a08a5916da809a8afd3fb1d78d9e16f8e37f51aba100d031d9da8613e9cd2cc621025b47150b3e76775ab23412d74334bcd79746cf601407481310a923047ac68a4e6a7f7b96bcd85bb6f24e38f03c80ad41a0a581b4246ea4715ee561cdc5384a51a6fc9ed8569ba6b12bcd95e6202ecf834dd9062ec539cc8cc4ed64ab9ff85998da0e63161e7391b14de47dfde41523b6c614618bf2fd1edd68a5de1c03c4181569b6c361d955c637abbf4efdb5dbf2f0dd2544329c44b77081a48f53231fa9d4cf6f2186427e469d0cfbcd698f7e7cf773240dd2b807a2fe699f0ceb4a2339e9cde01114b2aa5c3591a82a3a27b308e1e7f092af8ad97bbe7b28d78ecd80c0c0a28372193d66bdbadc0b58e4d5408acace53bd5e12101fcb25754f8c545340fbbd1328287044a29d18f40a24b4084febebe228b67cefd970df6d44ffdc033a50534e5977bda660c589c6e3c3a28b4c500b29ff4a1c3eaefb068784a29914ecab7868a43999833b0b37ae79afe58875a0425262e0ec7e10ee8a6bc1c97d332bc2a6195de239a166486a3c1ad8de3a026e5b1757f9a778a511024a260c9809ae3b22d78f18ac483281a796b1ccbfe7a9b9f357d12d340e20bdf2037e8bb91ef858cdf2eb9d7161a756d8c244c55524f8f5be2e4f18641bc4c2409c14816846c4655be716276d8356e516640da49e8412fcfc7ac0e084a079129b23e54952d8030e1f8ceaafcd322dfb4bd189bb5d940ac83231de0585783387d0642a245183f7a251779bdb12c63e9edbf3d0c94281140598fea9e73e951ce650c984cfb1398f8813abf8f8827af5eb64a65dfd1305bccc45086438ac439a9265790fb225c509ce3c9d39e25d2276d7f3c06d7cc28d33b2c21bb38b50dca5b10afc09da83ba12ee878e0f6054e8d3e78d731671de4f9d5a7b97298b01f37c7e78e5fcf5188554bcf5d42559d3b15153bb3cd5a0d1cc4a96d02ae8b1b115d1ff617b6ad894ce0585f46a2a5f4cc1b83065c1d7b5d2f25f3f4bf9966b4c7d7156931861d5edd199c126f1ee4ff6345023419d0a4c87f3388fcfbecbb6c1e2f4745922b88085d21d4551e4c127eb423db87a51c9f9a140f8a7415dbd70c4b0173e687a40f895404f2203e14ccd61e0e5b0d5aa3fbd1c8affb5807d787d840916ece24c56c50d3d9ed9f19d73f2c80c461b5b3c07dedcdb41402c3826a958d74be48382dc741dcf3e0eb8955397da33941fc47288147736d778914a57effbafccd4cf293e6ed1c7d19b55433bc0363e41546b3638a4c630eb35dc6a074f90185cb9daaa6eab54825a4daf76f49ad918e90e5777a826d6d5d52f32f7ceaf818f87251ab4d1b5406ae94e41cc97fe022b144f26335829d9c81725b3daead621a0df71313d18214ff8dc687a7ab86b8eec3070ee1ca9f62005a0cc15ca6e2f4fd893de8fd91f6210f6c96a576024678535c962a2dab06f56be377dfa74bca089adb7327abd05c3ad7646b5e9e6fc2f29916b34c8642f3c0caedb53b8f30c2a77d1757103b7ed156cdc703911366b02cde87ce7343886987f7a8c028921a7b87c5c0aca7ada34970a6d0d32eb1b177ed8e64c1fc6839b9d08acec19560bf4a815ca6187635f0cabb8bf062e8216d3b09b7abd99e956734129e16a7c4f3beb850fe2b1548729355f9015c9bba336d3e26a27b3d75d75722f7a8170d15ebf77f325c97778a5a9d7c76d3e101eeae354e54e6fa60b58cbdd900751854ede326b58fa5caca073c630f3719d6f52afe675f10d464e8b58e5fde75a4f225063ca48d76efd1b645e4bc89d98215beae765601f635a3bc8cfc08d74722f3d95ccb4cb4e3ea977d0c534a4abb866fb9a31771222cfd998231c30bd16b6844ef71038b67d72c910cca40db7260dff0b74162449a9e2cf15d7dfbfb3a685080e6c83ff4341c95819c0317502ed49af7ae688b52c9866518f74d69b4144500ab9d5a0829b9287d5fb67b78801119ddae7a76e80be8c4dbcec7866ffa7d081406e51cf617be061530b539cca7e1ef9118cc06e8eb2a01425b45947a1d2332e360acd0654bba8f1fa43ece68467690d36f6802a32f03f9ea056e57fd548dd4a3225ad5006c6c931aef1990639498dc88a23895ff1f75520a8009dbde4debc20ecc546e378eb7ed5ea3740d2244036588471d96e4751390b6b76b39816d853944cb5677b493b36de9736ba0fa404ad4b3a7d7c54d0c15072c040064b871401b25b88559d059a9519c7e2446b0d110a4aba9c12555e5f620680d1fea2359bc85cd15b5c0bfd6b3715d647514118cd60483dbc9c83e285192108f4ea6bcbee1f0935044610c68d052ccaba23258d09465d5521e2664d59358621ebbb8f28a4627362c8397f0a9852e5d8daf53a961d4ee66299e2b54d8adf5134ceedef57011f810aea76262422236c3e1a478a759584c7880fb3f32389c4bd4b637caad7b2bd6fd295aefb150754799434e99e0fd45c1cc4698ab14d1f63eeb06e53797cbdddd45e7f87e85b45a3dd0df6335c3b1addee87ba953bde29ed98042d745c1465a967ef922993798966e1c8b96ad6f68404136be0caa2264e24d8d93aa1b99da9316c7780904753d4e0b45cf282b43a0c91bc9ff83cb25cacea2ea72563b2e759b69cabbb6a50d6a0a5ba545622e5ce576cc301ba35afadbfd1e26668782e1d741feed8aae894b564a425141442fb8470b325cf7c8e1552973463bf4e67a2ad58d15417e418bb91d2df4b1310a0a70ac744bbb4245efd2ae642609079a44cbf6be19809a5ff7ad6847432368c9749cfb336ddcc0e6f52a699b910cd24671f38af5dc39268a3c87771f07d53bf220b7c2d5058cc7b0bcd492abfedf9bb295ec304107130f0e98dacaac6dab998b511f176d48daeb81db53643ad194690b6e28c5ed2927e09a1e959c494b90db401681f67bf1e23fe9ef4c903f666ef39332a91a25c63efe9bc518e9aff61842007dda72dcc0264aa47543c0a8b1f0d25749ddfbed487282241140d4c64def1831c4d75ca975fdb03258ddd013445e08bfb479a516b011fe3a12e4bfca439407c0022889e46914cd41a4d92a25eaa57a55bc7337e5fbbd11584dca34adb5643105c8171e53cf04b1412c3107e72330ebf1b52f524b4e72570cfdc0ab179991f3782d05091ea57b1a233048bf062e88939cfeefb61e8beaa90395faf61c4d974b23723a4a5cd39d70f92620f8f4f27bc99ca67bed7fb6e594913991ca3025480ad791bc94a0def36fca491a206440ec31e32bb85850c3606c875708309be63c2b4f5c477521aa08e1d059cedaafe4fbef5523b79f88b57d0c81bbdd09202095f10f13e2609e833ff41b862b2214c22e8f2b04a363b38d26bf95c07b184b9f909ad3a92122e158d3566d2204b22d4f2f3ce11a65544ccecb01a4a5ef62bc969fddcb648224a5c7bd94f8da9a7d4df393d880f537a377888874c19dad357a0564d303a5c1485c1451ea55d68779dc0c11c7c38025660684ba3f70cbbab00d15b34c0f2342207ad548eacb32ebad95292e85211a8669b586d05b0d0b9f278a35ea4d78e97fd5dafca6b72d8e1fbf3e704a60a8ec60befe2e3e4d3d37f9d33a0feb88add59f0171ddeba0b79a52feb9a1f4a7a6ee7c6927bd10968fba788a807409346a0fccd4f7daac3c8591fc689aed881829d479e8d360cdb5819d5eac718a6f860f2d9ef6a0d36ef6e10efbb37819bb7b03ab7649173447b2cd47f3433a2422b1611dff91cee0b10c6d060d4e84a9e3f4dc194514cb67f1e3985be05c845fc92b41955d0f61aeba6268789998bbf341a8b37af48f07b13a676a11d27330529cabcd52365842be559857cbc2a63a4ea1c77fa8619040e79705c5b51f473e13b73fc09c28598e070dbbb63ce884c2843ec365d4c5bebbf815ee3314dde0bab6b0a71a398e2d9ee8ba2f832863fae7eb0c18adcdd17f1dee0df29a8409acabf516c8e6dfea5a264c1c6657f774c86a14cf96eecac18a41b1650a9e652c6c9264b03aa2fd30e333a9f24cd6b0313358e3c00943a1de63ca970b7da2cb8a0fd1109cefbf12176f5dfb59457480428b194e88449bffd8b8d87d05d30f9ffe9ac3a7442b0df3418acf9165b14242489a54b6b47ea543fed5de74a00f61ab2af553b60d8d21c76c42052c72e4841bd94cf88185c39287c04d05f6336ae581cf7528a59b2874795caca79f5600ac64ad5820a91c711ae5a1c3762028242c5c8a9aae89177ec4db5785cd07402d45805a2e2e970059e4e6483074df1bcc01f57470fb66f45ab475ebb5343b727168e355a6c25d42384e39802d7b4a8c54ac94d82de12f8de13630ec8c19f008f98c505dbbfb21b363472e23d0147d1ac555f0981e2bfd07c62d097acf930094dcf239a40699421b207ac2575b7edf9b1d772ab066362820c182c2c5097a47d1dd25ca9e0dd9c3ae94e9a8f0dece74cdeeec3a17803d5e11f037820ea20364234079286a7c291f3424292b0eec3e956513cc6b078a76a3b8ab42c5fb5efdcea1d438f7ae08507275b48f9588a15be763ad094885269efa7330f6fc9d4746997c98d9f5feeb6dff2734d75afc6a11196b35bb9fd0c0af428cacef0df2c5ad4e5fb4559f0f93af2fafad6fb77f453238f409ec71a912350d7b62952e4858927f620d31569242615345265ff1cbbd7fdfaff35a45732628da663bfff3d3af3d7b537337754554458a2d1af0e16aa8ad9436096f42e243109cad32fac1adc58d714cd3d0d8483c783006991f3da263ef5ae1ff2ea06584e45849d64a07170675c29f0b2abcf1eedbb63b6f5d9dba600996c7d5edce9ac69448d05c0704fb9f84e831b60c376b8a5d33ea22030e2dd3dd421d8e0a810a77c085a3861fcba214a8baf592d624d673fb34f906581d923d80b06186db8ce5fbef2bb750166f7556adfe93d4951a825d55b0bf92c9f25776df784f6aec808ae221cc98d05ce988fe6a13ff96083dba15500e149409b54345274e3633fa8f6685d6fed40c20a5c5705f8b37099a5949846ca15def5a6a427eb4eec72747f116366adcb9b74d3de0b125bdee23ca98ee6312f41fa3d9bba43a8d343552c969c41f766ab4341a42ec4cd6f4d1d4c4b1f16979e5389fea36a150580418d95520506fe0cb1a1d861e09d21c57d88c46e10a3c5ad1aedc8f2743f5c06f10d6da9b2bb3ec783c6f5788ce9400795022cdcec197f9dd3ee4cd26531e7f057b6d9418a0c52ecdb35a24a2a079b3d396017feca8b31aa55e3d5ef79c9ea9ccc7e3d0b47f28f273276666fa1763b3a452672fedc94557d984c3353344a8bc9fc833dee685e33d63540d0801d8068cf66cc48ddcb0d42cec881eae36fc2614f96ad67fceb5c98ec33fbade0e3049178d503c13c2d5d71f32f4582d1cb0f47a2ace578b903796768a906998bed2995798251d7eb92faacc19255bf12c0024a94971c185841113faa288beb7e58d4a98289630fb3d230f936eb1b9d9c7b94b5ac9d3a211c0b454a26e29bdffb522548a65e8dde3730918fdf0575245e71ba013ce08f6e698342a61a81b1355d2483e97c06462cae1cdc7787f4b",
+    "cee4396a08dac9c14981f2a8f4614a31b019c83782d5d8370acf9db467d9d95e8efbe44274fefde5860c2333cf81593a2ada9f5bb6c2362ba97fd7c3e5bc836c327c66b57c0f023efc0c0ff6feae0e625df2f4e21057060170c844c86412700d7d337b1f7835a0dafee5206cbd76104c5a36623c7783213f8dd457b5e69a86b74030a27b3c30074242b1d97e65a233885a681ec5a8532bce9dca1998dc32c6b40dd997b99a6ed6288e0b9b09447e356bc5345b2133571e65d47db2c736a391970879103d4137cab6c0724b8e67064167cd5521f32135fb6ca43c1e118adcded8227c9dbfdc18cdce154108eba5d8c60e5362e8fffc5c9ae6ac2572188617e4ce0f432e2476c74a4227af64b58e0ebafaf0b1ce01723ecd36a2a4167b7991e28b6a9e81992fcaf7b4b906d0361add02104db83914f28baea26b50561faa46293ba5247e8263ac0347509c36405747866d2fa2beef44f366108f6a4047e282a477c28654511075ddfaad9b9844e18e67320a831e647d923b2720d65ddd9ece165c222231d3c3e7f0001d15e3c690e9831ceb369a8edf183133814bfd20dd25d50973bda58ad03c4cbda8008556fc653ef401ff76ee858c1f79a0b09b4232768e72dd06e42078923d5647cb310bb644feb24d6b7e9d1167c3676cd96f79965a066aca314089db60bdc40c2be4b69c569ec76b3bb74a43fe731bc869c9222ab5404304a513d4f7d2ec5af278f7c3d664fcde579bba7bd472bdc00a1eb4c46ff69fb7e45e5712919e8656a8887afa28cadd66461fc57f53d574c92105818a89f210d7e8aee6de2e78228b2cb03b850a6e77627f70f51bb919bddf61837a978dd4cec2db138c657214ac07b67134bd53b071e2bffa3608a0b0bac88b0ddcfc1ba4dea17191c9ad76ab8de72118893256a7e13e15a3bf98bb5757a78c58328cc4b380f3786f22c6be81884d213ec3cc2784583a47a4003a59ebea08bd06e290a892c937448e664dac672942b068b839593c442f6e1d22875e01859cd24c17d108696a3196ea4794ddfdf25721d3dd3e754d1ea884e5086479819452991403a39014297fcc734e56f8daae4d49d5c47016fc3ead550783df895542229ff3b034b5b722ae2a2b04ba70e42c174e9ddb89ffa60024aa16f297ac9383b2ccad53de4bbe4ea2fa3fe3d059d16b4b4fe9959ba3c4e58922e7fa2673f50be5b636ee7c79b445471ddf5b851ec3ac505980bb184c8fe44c7776ae9aab4e66ce31fe1bc00efced390a82f96b4866e31ba3ff832a25b1e1d00ec44bf525ae523b7102ba60c1d3a2e2bed004524afc90a064b325a258eb36315b1496c748f5407e922914787acb8b47bdc495e521518e0637eac4b1b4fe1adede145181ec7ef038d48c473d6f296b349d7cf874d329c71f272883eb7e77ff303957e159fd417d5055d82687448950dd149e1074a1785518ebaf7ac167e07f1f559893a20d133b59aa294efebdae1e19a30ec9a3e257203eb9a854096395825ef4d1e4ecf1f8daeadfa049ea6c435c50d67fd21c6f6b11a8be46502f0dda1715f5349df5330454316498660b7996432e679c73f1af33e529ac669496bde538890cc093122842e3e2e4bff937708dd4b1b1d3fc066a63824266461e4af9245032d690aac0ea5636c29606473820ee57b112e2bd68c0ce1936b7e76a7873cad678b26b560d7bb10a7dcad3f69bbf226faf2f572c105741a121fa1c55ff30b2d0b7339ed9aa4c9a3671e6e4b572800afcbc8764b16f0a61c4c1ff24c3b64992cd84f39d1a4d5532a7dbd9f7bc847258a33c509a945e53236cbc46b61fc6fad662c523eef0c1eaa4bc0a49610c8d09659e7bdfa858d2494dc3da0a54fcce229951d366fd17f4120f27ac77e5e6b777693641a853eacec09cc4dc08ff6ba22295acec61c5e6215eaf2a3a012461eaade8faa9cba630c5ce2bee6f1a4676d54b4a38b7b5cfb6c98106a4882ed88153a4f0bad3e0f3d04dc1ae5318e3b8f4ab1d122a548eed47f70edad1a164a9c5c3eb10fdecb24b0b68005b2e958980481834c4f673478d3f47d07836d3c1c513dd920042381f70f1a68671acee2fdd453a7552eba497af27127999a13a33104f0086390e01635d1a0b79d92dd43211c74047804e82d9ab26f97ee88e664871dab52a2a79443e39f06a6e8ac9d5e986252529b389d9ed0b2f55fb16ca65f6e90cc9a149065f499630f973996c1e2b6c53f2ab391b7d78cc6926b1684d066a3a74b86b3b633baaf3730acd28deaf18fb926e1ec9c1f8a2345103cc4cbec05345db57c5adcf062412f289607f5fa41194f69bc2f426a30c7a6f8d1027ee8dc96c9957e90fbd9b16475b82dfd8698195159bd7b4860004beb1fa85e6843eca1acbbb0b8c7ec0b865ed108e297a2d5f915304167e18d01e51497e6e3ea76ae99bcb849f7595fa74c2a6263e2bef65f1063bce05483980ed51eac5289307117f17e99d761337e9b1fb625a1b900e6179f3b02de57a0b5f52352298c8a2d2c816182ec169d2b9c0490097ad98e2edc99c6df683a4b5b6eb73ccee0aaf07e8cf8f2f632381ac407c5c578bc1c5a8d0915dc231b01b92dbd25c2bfd412995780582793736572f1e23ef690bfe6872c2572285cd737a4be91f4dcafef09232de77b315d73f5beb23d03625e031d2438081222b063c343f52565ae314ac47a4fe518b45d0c12f2ceabc5e05c20f607b97035afbe0e29249e47961d9cf9b385c065966b0c7ea91cfa9ed1b55a58b9aaa9de080ca05c6405fdf15bcef74177226eb225a47d532bcbc82a5ffed7fd86c2609b146d86f566d0b84f638d46d6eb696bfbbc62c4fc981a94c1d6ccb9f3f7ed7976ea7e8ff1d2a2d79986fb27f1401f25d5a83f64844fa9e839fc8855007b417b261d325b6e7cb124b27ede8ad18d2b6da8bfc4d4f50c3960d5a1c82e4557b16d05471602d2a31462e4bac9535c9a57389ae0613a674815c2ec10c19f529c9274896dfe49ab06889da517d482145ed8f57ba4b7c0434ce24090ef2459682a4f6342ecc382b4cd3409c3415bcb7f1bbaadfb7ec308eea8b6cb2912469b707c99a55c1754db0650616754735b85a41433a30b28e3946754f90caeb03c7579fc9982e6ec5501d6f23e0f2b6392acc435907d79ea11eb6955723a81c4f02bfc78e2eeb1d0408f8f06b4d2f6d20d90f7698c4e58bcfa993884424f8fcb602ef35d23737fb6aff220927e28c19043ae708fd9755256a8a1660d9c5827bab1b836a10aa23aea9c92fa3b25428b3791c5d25f3f1b63befd5480ac4192c966350edceea8938ecc608e0f063d16d427049ad62625f5177470e7a0d811e8d4273aea8f7377d51db07fc34d9f18497a0c2b5c0bc5e8778e06bf7460f0487eed54d661d74346eeada9090957159b86f8b68183e33d0c3fc134d87e068badd8789d4c7adb829fe08e4558bada5ef3f526afb2c7b6184244af0d07aa5cb525c519ed32298bb6241d900ead0532b0b1fc77a6577963e7a44627ed326741af254ab957ca0298a74323d2ee4f1bca70e20ff796491424e108e03c20f2eed7374c0aa2474a91f3ced6f46165c886a510734d606ceaa08822bfced69def33cea3662512fb42ecefee341d1b499b826ad882542374b032e907a7e6a4dddc4620a5d1002b5aee25711fd2dd6e9d4e90ee350f2889d6c1f4328e4b711fc919ef3c655311637b83b4eff39c157e0510807ec61714b843bc9eb22a0f4dac7e5cc07b8e9ed587b701d9aca2a239e76ac9a16338b74d50578956e06b1ac35ee3b822ca779922d89de7d915afd7d80831e8534b8f8a2eaed252fd862abc99aadde62d4520d9a7c3c3da86081fc36927de60e3479096a2b5025b9a789da01da969cfb0ab2f252c82db9e6663dce3888146b365080f649cf94f991312817147d8f0d1774d8d44ba4afd846060df2de1d1043659c3b94b1eff51fff84e5a81a0c635aedcf677285e0d722e3335449fd0f49a41264fb963ea5bba31dae469c789047812071d8853291fd8003cc31a8968ce7acb68a6e0172ba6ee0e9dedbebeb62143047336c5a91c77085afb01fc075938b306d7e36383ecdfda55b9b5dfdab53aa34000289c398f617a146c4a06404737600484d8d4ea960061ec2cb575dc485f65f275540d0ce7550da08417632b6f0f7d044f6f719ff839aa3e5c9db94d45225a1cf0bdb0c5bffa781572ad605ad37aa988240858c9493dee9f00ed281e93532d89aba5e9e59ec430cdd5edfdfc2ef65e094eeab71cc40b59c997943a0e0dbbc80f1e11834bf3b53153ba1c1f0ccf63b3c802439b2ef1430be6994300d9b2efe4b84e25bd3bf8a566d4851e7fff57cada544d722438e8980a31563ef0558fdd8db9bdd6f1a3e34f06104b680f63c1f80a08ec6ed74bca69bb1023fe63d24c7e7a14ce85db6e21173f2ddf14f233f3787a37e4b347e4d64907fc0a23c3da017c81c27df9fafd4695886d0ddec8c47982912eceef886ab5680a130bfacbf3c67bb4f0cc118274bdfed43bbc2ba56f048d6a390e48932469b30ac84fdfc2e812f32d00a85349bb22f2d8091e64282fca1b40811db756059de5d03861d6a22cfc6289097d23c26c5e3f000f9b34a0e1b28a1269d8673d09107b29ccaa1adc8939bdca312c69ae4a238f45410d8f1b27392d594ceea2a6b42899ee5c5857965b29bea1bc413da618899b1894f2adff3b3a7b05a626e50e42379f5d0e0a148ded33d815f59d1401b197a85656466eaf88ed30d1ad4a87985570291efbb3a2c6f22c0b111e65c843ca3c6179e94335f0f91d4696e1a31107948a042f55f264c32a35e719668483957c9c8e13fd01e5f751870a509f5f06ba41ad63cbd5f706f25b1e598f6c9709ee6bab627211bc38494962e930779ed4ea2a8471d309c4c4f0603238959cb13476b673489696c87ad9da5fef0d6467145a77ae0b1089c8626988278a85be3292680d9d7e4c6866f19b78595d611f15f9a5e37b3d145d5aff4a5b58a3286bd25a862904817afe8e9b9105584af15f54554ca5e7dceaa0fbd1111aae126d74f68bb6f0ce98094dc9a59a31d9526729efa171beda9ac5b7db9118aa94b9b5ad58dc20ae1c328e31269244d636139\nCiphertext = d248b9e47c303f735b0d29f6111a742d93509ae051466688d56b587104a74fab1b259da64475fc0d2c3e28d87ca4edfeaa5715c23dc0e5281eb0c0c14e22182bb02f9f7d3c24555cd6a3ff766c774e67730a920db5f85d47dc23bbbee460f0922cd7ddba81ccbe727b4b489e79a19db2d012dad2a732273dafabc0fbded3c47dbe5b6b585570c39eb62850dc47f4aa0c29bf5fadf334041fdd4658fa6cc29a81192a53dcf47c03ddca9d03b33b06e5b3808be77925b7e7d8cf51fa939e023161d969f92430917d73f3aa10b83d5b7402410280561a27c376ce0b5151a51be2ef4eb9057eed25a0715436233615dcad1559fdbd81042544441857cdf46d72f5f50ee552cfd3bf166c530e57fd97f34e2e71bff8a90b30b4c4cc3e843b0f06e4eb2ff82675e428f5303aa9141dbeb615cf6aca5540fd7cb756fe5f9b08a4abdc6eb90b2eaef51c21eb9ae79a0e44b0755b3ed48f5e6e57f3148ce02501528dd3dd2b0bbec2650710a183e38510990002ce6498dc5ce7bf33d699dd18b66c0f8031d958b11d678674c355a635f4b5e8d863785f5dc2f99eba9ce74595493c017697344b651dcc2a0b1d5386b73abd8bb2dc77a2d92173d3688d0d704da9e44a6385af9fb3a81db68822b1eac9ab284f0155c20f",
+    "6bc34af85d8518d0dfd32fdaece1379abca339a00e1326b624b3e4050be5db8dced5e6c4b88b82b6ee2a48c373d236ea3565ecc072e953ffe01b624c6ecbf534678aad9c3f8a07d7dd7232134b6b397d0c96ab5f795f9e3af65b96e7a765283d8081dab9f953113abe06e8d150bf9a8416d8932fac17b032dc346be43736dbf066ed239328803510f6f62bc8abc92f6df9a82c02cbf85de91739bc8d7805d392341be99798079419540dc952fa0d3ceca4b806ab1db3b717f0d720038343465a8bc0da8e8964e58634e8a2d6c99230af2ac7c89acd3f86a22075dc40818028f3c632b36a39c0e064e3ca2a078c617a3e73aaea56ef11114f9efaac90a3ec8f8d9b18921a80d74b09ada83efee127f41179dc6c19c7965f3e7f43e22f636534b123e9246172f9920f253d2a2652a5e8c337ff93b2d479bef5e96e972a9b9cd8af057c750bd711010d59ce065ad50fdd487b5dde616301d0ae6373b6f9efae99d8972f242dd7a6bc61caee70201869be202fd384a992478dfc133b84171f013244c5d17585934aed3b43b818926246227d255bf832ff481f5f8d074ab159a11d6d17ed0ac50f727b870db966e0373bd3b1eecbf9ed66aa66caf33ac57cffe4ac6df3cf7b0e54ac54be4f3d50f61b33557c2990c908a710c85000ef6fa62716960daf918ae3d81ee60b3813e65673bd911ce468510bd230b9c2d215afe86ec12e49e0ae87e4235baf3df237188f5e0af2e61c22a4bf77190dd5dc804b4cc330b360c3dc093ef208c37d299ea0cd2ba906084011e16ac5f4fc9646538d5b538a99546d34a4599c8529c1524fc4b394d6a9cd762855905233ed92e72c8b538372ffe2f0df7085eb074616c7e695d7de40779e384d5fdb49fe02385424ea991dc05c6ae813f76c673eb45d6105bcdcfdbf04dcdd20caa6e30efcf3537bcf72947e1ac37d1e8c600ba9238569a4b3afa590d61acceb2572da85885146a142c8f8c60afe4d53ee4d61f33c47e5a99da9a346bcfbd013754ef39a4d7f16c4c5fbfa53d7f180c16e2b64f97dcfd65349939b5fc167c7a78926f638f1893fe9a81e897beea3258a4175d14f41dc123ddd846e45a87f35154db8a5ac27c7a0fc95d3b3113cfa9e7c828f83a1f0d91ab7789c33be5f55ecbb8eb0c81bcc0fccc880011b21000e2f10773388b198ce79c5d694472d3ef6b2e55c342b29c70e4f33fe59e2e0bd3f9ef617733dd3329a0e426338d9c007bcdd382522cb96e59b223825a39b01b52809f5e8518f64b81c99022d8215b5c435d87cc1a57bd440b31a19b197b277b2072968595ccd64c135ae1b218046e27a7f2685d013ce3173efd07586dc72a28ac4792e804d44f9efd785ef005213df928560a20daa4c24ab07f081479270a0dcee3c26331c48a164e4b9d79a7c30c77ab06b00e9b72c190d35fb873bc095d5e6231a89b52a0737a99532079bcc72ee221b48f0d0d9ba9105f981beb4225f6efc1230d6da10fd2b58a65112a98e4bbbe1accd6e8589eb6d9c771bb911cccf42aa6cbc68d1976f0da7eff1e70277e8c5f83734ec1efb2b00708fef08e986bd6519a0fa4b5772e585ac8e37fd2a2af07aa382579498b3b75863fe792461492b8e71c4a1a2f4421705696a96601317cfff1632784b5d75fc2036ed3fa650354620781b9fcfd53f1927223fa045edf4abe7b2144512f3e3aee99f7ac3e46028bc2427aeb18e9cb40db57b696ea884658abc9b7bae0d8117f93074a3ef903528f8b55c7687cf9f0119a1f246cc9e993219c6384359e7e5e639bb294b264048060224ae168d7b9f1f795c07eaafcddb10b61ac2be3ab3e1fddf75c1f47559f38d24f0c773d0e8bc5fa85d7d33e3aa8d0f15583b8c1e7aab6f5d0e085b7175678bf11cfee8eb069b78220377819e3f4d28eb833d3d21efff543d5c6357fffb4a8fdd6ce399fca42e2d71c53c50f6b20bcbaa1650b57ff483837c39a37d5e978393c332b43021508b8ef27773164d69d0af3c0dfdc125cf30a7c49a7d8e5320d68a35e80cdfd62a0b7ce6a412f08c8062e35265fad5d1f226d590e9b068d09e48772711d7dbd786a38c0325b3d5665c2ff45ad0a20c174dc5739896ac727b34f11c7af299d36d30c69bbdc35770138cf891cfdd8123489fdef2dfaffa9c2548ebd60b0f0bcedff44691979b4e92b364753120364dc2e3b895095da828e8659575a85cca587ba05ca625480f977a6fe10181ab6ce005defbcd8894f8c71811909cd6b56eb7ffe327f46793a9e98bd7fe8951400276bb9c7607f8ba1e633034b73d7f0d040197c3f346394eba68c8accccefe05f59cb7ea9ab1ae2e172d8f466ee21c6531cec2c9dfeebc477a6d98195c28bccc1d5e23ae50e3a1ddd7de189e36ffe0e387df7be43427b194b16e18b42eacd517bba78edc9f56a2c7e89e6f13513718869da7c8c529bc337217a69e14e35cf97ff7db2c23700347f0a33ad25a299fc52b35f63949735ad864aa127053797541864b07168f89ffb7ba5c9a8bfbcb4248383a95f45461a7aee9c658c5679205f47144ba4a06175e746037b8cb6556f06405e0d537d0f2bcd898dd5fb987d96dbce33001a50abff5b9cb0161dbfe30f5df5a161ddd8a750b0cb33898c110415881fc81239f2e25440bca41a5bc46fbd3787e6c8fe8a463415cd9a82be368a02566da740dca8e40e686e1213d9c15de2d3556a1e1180b298ba3074b4ab93e469dd9a39ac0c8a173b04a5ad913e72e4d7b5ff520f108e1a1747c11b6b2fcfaa89b3ef7e669f8ad9620364b4f4f0f9ab274e76bdd631df033357a24723653e427324d907a9eceb3c375c43ee36cdeb046a6374be19ab04922da93d4dc07c5914df06fee97dd813f5fd501ca75e3c5ad53574837f2e51ba6a257134e8ee0f4127c59840ba8b1bb13592dcbe47aea50e453c7837e91bb12ad1c74fd0f149479bc0334c511a822145690a3a408caa32671ed05c2dd219ea360c67727c1fe6a6cd842301761e94bedc73f93de7091b8b6d2783a788313b2fa12595904bf5d1167a5ddc4ee151b1522de60b7293b72a62c4d08b396ed682b6a6262a212ddc8c70dbec1a972cedc09f593e21d843279561884f9759a593da7b17a147db7559f19d5d6f43ea98012872f974306037dc0d344c55403b35a5903f766359341bee5bccb696fc0fd1c7aa8803e4c2f9e6e23d386d3a202027c5792e355592efab9330af330392a7c91e3cacc4e645359edafd78b77829374cd4b644817322b7650696fa763a0cc7143f9ec7e2f6ab3c9ec2443b0c0b0a31e9eeafb7bb8c375232357f08256959a10a6d4bc98d6cd9314a2ce7feaa8c0eb1eeb15047f715d6ae9ebd64238d648ed6bc50617a360d8ff9a01aa0ce0e29338d34bb9612751445372ac6d74837c7d2d67729760216ee33476cce1a154086ec31d986cc5a14e86561c6929554fb280646164bb03e8e52588a1b947960a77d61c2d2499212a742e1a5b78805b5b64fed141d3c4834301b8a8bef31ce65edb539fd9469b590a6980d0d1bd29e34a09f87438059a09b1ea234d1bb29882e67599fc1e417db9d86332077cfb05fe440ad1243e26a67a0ea30e63cdee8850a543d76e810140547412fb1400ac87a10e3bc77d3918750a5cc3e7a0efbd736c7ed4139cd5855ddba47143362bf40b91fcbf27222017c1552360466483e67ed125745724cc713c713dcf7ef6ea3081d65d8d78b903382717848bee7410431e1040ec92373f75a1bf229816f55dcfffb6e6da33ed8e1e8b05f9348cdcd6938f053eb9f93e0de639e922627bf61a6688f9649bb9cdfce6236a176db8b9b53ce4b5f9eb9c0680c92128bd327aa7f04a745025faaf117a18d5664027ab0e3f5898b834e1a75cd4b4087637733416f8bac1ccd67cb4457005945676d03f76fd0453fdb9968643fec98d28da7c8cd7070a803b14a2459f073ea075fd023a896d3306fdabc54416e95907103cd2fb642e301c71cc48e8eabedaae356582761a14e0b3b0ef1de06002c2acf594c85820ae3a094e5b4680566b592221543c1dc5192d6b208e86b5aca91d4e3454564eedb3b8208169ce97e1632b864f1d9d4c4c4c0fd4bcc5206e8f6d64c7cdf212d718cb5b7c7ee21593ada3f33f5952e12bba4f46cb99044978fe75349c6ca735db35891351d7e5f02a93354bc45a9ec756453f053cb87430b3e9211807f81ad99b6fceb8ef1b2d655910e1f5fd22f2ee90e42abab230f8f39a8345eed6ad294a0d32416a253f829093ecae209bc1dbfadae04a373080f9ea8394a28ddfe1134309bb53ae571d2019ff2bd4be94f8176d90987fcebad323f0b2921b85b2610852973f383a2ff4a5fa82a77b13cfd50a33f29164a9ff409422cc4cbd772132856cbd08470b220ace957a6b8e02c8003d750539a38a8df19a5b662907b72e3098d77c2fc3ece0693b47ff19ce911a93b6adce75653d48ace6af10b8f1141437f9206658707b16794e349db3f1a02606ea167d0213ce3644f64ced64de3799b1729210fc31ba1811b0c226306f2466b230ae35e6d8fa11c8f932e27da8cb1bd311919bf9178ef08bb7a2b4ca2d2e6e9585ee9f916991cfcd4862f5de9fbbc63bee6edbdcfcec9173a252eb59fc6d6e58258ca8b2a4475acfc1e09a0c9566d23d92e9ada97de51895bfb0867c42025c8d089c65bba67f4dd84d7c5155a930329345cdf3b1d6e910e730df273e183190beb900344bbce8c3bdb13a7e4ecbe967a61d47921aa55bac2bbb24e3e03d386ddbfafb3b32235b5ed922ed6ac2c89ded1316b69079b826507d708a6cca14ce2244a67be90fb91ddcb0c97432703729bceb432bc856f5eb9d2f169800a04283b080f0e053670a21468df9414fda9f4153eaf1669a19ede7925f832280800f0063ceee34b9d3b0f8da2012525fa7927e76bda71954714d5f51405b920391eca2ad71160acef4091878b907974573b4cf1b377baca0340ab0e4ec546fcaa6130603ad633c3ef980e88d8f44ec5de743cdc6cd9e0e4cbdb97a5c076be9ada8f26bc54d711facec16a2401292cc167bb98cdd320ec9321414bd97498f6d9b54dbb45ffe4b3e3f88260657ee23e19de48a93595c8e3a289a02d76a27ceead05d591633464709aca117c26aa49b64667f2a3b6371984f813d7098fae7a6ba1841775b52314a06c80b4c994ef8100e233ab3115ba2c39b97f2d5082a145720ad0b12b8a7cb275ba848b3fae14fc0c82bf0353195c056b302e508982f73a8519cca722892482b9d9e6a58bfb4d862fa393eabe6aedeae1be5ed772ea3c94a0df1d9684a131c35246c68b32e46aaf89f3649e58b2e99bd6bb3923d3ab43cbf73b6b3d19fe3b62bef178f46c79ba85e23ee4b25bc561e8fa97f51605bc0b210b02aa28242e81dae9489076d259f17d25b93b0e8a2010584d907314e3bd55482f0fa43d37ae9535629d28d6f837360bb35ec869d2a959789dc49b9c8c515942a1e03650566b736551a5180a60279bdb0ff9c387beebeb9e59ed930b3746464a010a6f7ef1de3c7d76fc6899b1e5ed98213813ffb333d969ad72fd8537ef4e12ca7b78d35c24f44ac82da4a7116492ca2efd86ee6a4474014e72a5cfeee7f729b77cfdd1a5d10a03f3cf28f1d314fca36d31ef2ecb3cfccecbcdfd22367b0a0e04435654286ae3d4fee13f56bb7cdab40b4e1dd01f9ef857f94a67c1e237e24819949935ff3bd73b0461ee9020fd0a2db2cc6312ace97e4a8a33c295271453a12822db8d1438f22ed0d466150990dcb39ed042424eef7a121",
+    "0c83224c856923e3251484a81a15cddb4d7ada8bb7968dcc8f85e39ca99ece8ce2ed7753fcee6900cc9b7b5691f2d67ef9be13f70d195bbd0047908025df01b4f4d581fd59239836578627d9d585ebe9b053d807e9d3ba25405029a148938a746636decdade02b1afb5ccbf2f0e14a27c98a1e130d9208bbf7da4bb4e572927eb348568921d4a3309a2c24f367c935c2a8e1524c3024ff350ac7da8d2849586817bc9d46a08a21aef035a6151e608ed93b1556a484e455819f9ac2fb155020738962e7255a82a0854b31fe20cdd351c10a33eb693c9be1a51a932e04d0364ced41ee1bf800d0c12ab5eb37fe52563666e52827720e856d4f24eb06e0aba446910aabbe36513f2274362fedba4c19398433029495284ccb499bb559a9cdbc94a0d1b733136969a743945a04e1d2d4e77fed21550af35f22651c7de802eab7a3942d7ec55a3a5002bde8d5cccc1d4ac4bb7f4926615fcece543fe5d9092d2c4f50d94fd9868775a072f4a5bcf2e5fd10795f7f172a3341ce33505ba68e7ebedc9c1e9165864244ed31bbe5c308dceff858cc42010ad8c281a24689cf2dee8a549b1abab9981d70a912174944b403ce664d8608b2f723150f5c12164e4caf28676e7a25c3928ca2a4dbe96355ef8f282e57888d40715df07bd8b5895549ad957e758abf868def1c1f5e260d26498616e2ac962bcaa33b879874569f198a91ce4e50fc50da77fea1df9f9ea900c834dcdd462d338efcf8e612aedebf254fac596507d175d30a90543627cfcef6852c7cda8b430e255c4d6d417de31eb5dba123e3ce9e2269867d9a94fdcd8ccac40a9451953085109f5ae0c3e04daadb4a2a47b0e176917660eb3c9f1aae0ec6b00635fa387e056623947c0621f0a12e86fac1881ed1dc1b9f523388d6b6596a152b3e732c561972879dcd3f0232ef0773a4fb195a90c3186c4688ea58967ce7f18386b80bd38e90cfd4cb899337ab27cba8db6523e979b4c449645bb2f320ccd28578bc7ec38f47225273fa61a2e5df97c4d76c556fbe2b0fd30e615f5fc82c3de7194caed9f5946c151c22b7a0c48f4a7cf78aa153414f2913c5eb95e3dbcea7ca544272cd13a1c52fa87759aeb430aab144fab418c835344605df3a044825965ca15de6ba0e59b2080f5844b2d110d71587e19acf14264cec2de5b8c77d18893215d1c1da0a940e7c2ee429a99e2633c216aecb7675a2314a09044951ca5a8eac798f8878fb5ea65f4ddccac53ee0c786e597169079fb6e8ceb37a71580b0904a97450909ca454a690821e249aebb75449e582fe1b30f1fa9f6464bdef654daa5ede6d4f223f4589ea25a25f4672cfbe974d51008bce296628556f55d26646e40b59f40e3149273760b40806ace3b5171e0b79865c6adb53513da2f24c4115de243150cec76107b48ca8da19117f00b5870e67eb8357e43c1b7b593c9875795d46ede26a109e05406b69fda988947e49ab195f22454c3c743c2ec51b91370b4df8d38653b353e51bb83215d122bcfa591009c007bbb6124bc590fed3f9c5699180b3b1424ad02f7c90a149b77d22dea5c996aba675c2a1a20e206d9c25d9446247d495a26486c0d0bfb09d0b5a1a177a09fa749dc36cee73af0116a6b779c2b827512a04ff0f60b483edbcdb33d2a18339463c498ae67ffa9da0aa3f3beb6bc99212f9e6961afde89045520b1f3f2e2761666a333d76030f443f53322f099035584a60978ef8b49f46d7d4d8c5c758ea52a04b59c1a3a1c2f9df3f3b6f5c45cf4b3547043b18c1d615a2c965c3918d090cc72946e8fd0b938e60e03464f4bc71fb719a1d173b0931930e58bf7f6d4403971d36b40f83be6b57244a7029e1d41dc908764d57a5442557218b509faeda4e9fcf31debbc54ae671ef636871233f29e0013c0e33933543f4b59df1978ec89b109c3977b0cf938b7f6166d6c93be5e87684a703c8b7b5fe1a8bfe153a179b55575ff05e599b39e32ed10d958699a1ffe07136081f0719b18c69dc74f66f211103e9c544f3c81a88ba9f66a9bc7017d9ca9e2cd97634052694a598476b99daf1cdfb6122869375ca5873d32d5c1e07d9b5b380b4f09dbe04478cfb1a13853eafacfed70c8abcd444ed095f78d07c0e8b4093be95c3aa24b2e5b6bfe3a06e9d2d9fedfcfeac4cea2490627e6da6a5cca383351952f654ce2b0ad359c0f7f4ad3f8d1d4a030a947d4a2e417bb79102729115cc8b6558c3362b1d805fb48ce4858deff97677e60375ed13e150a12ee7dcc8ccc64d9710c7f516555c1f7a1a08f0d7c6fd21f864fcf28c8f748c40494e01fc32006f977a5100577f86a484d11b82c90cfe6b4d6b1902fef486cc6f3e033904e150e67283e49a5382961dabd244412ca9657b48796e476a82443167e277d5a65c0c563a6abca77d316e5d3ab639a1ecfb1110af2d29f146508bd9874486dbb56328d6f59479e2766692821660462aa60b6bc8a710707ceeb0ea6429e5113e03c9f41ce0d69c7589deb547527673e8a9f9a9a74e9e4bbcabf2e306b35504c1da99730ae86e94cd047b2e6ea5e97e63a492430d37ec446434fb3b066adde08b17d7d903ad194a4a863d6cfe181a45c8c97b5062bf7c4e44d69c0d1a7e1f5029b805b7c21d1b5e56e697999a32557870ebaae8d87dcb5ca5eea2c5547a16b3f30ef9df8df821028c106f86e091050ff8b6ea4171e59dc2592d405073bea53f8ea62edf112dfbc7ca69809db8005783d63557d3d90d123a944be395c1dc3b5e1476dff188346327769fea65f3cf9363e88ed67335870ec8ef13eb9d9ff5317c4e24dfce9d11699e5f47b4233cc8f9d1b915e716a5730a5898ee65d30b1628b484a5e82eda95a590964a8d8bc89dd3c5cf6c4f9137b8c6ee9d6a692e0c0d1d858dd5b3c12de48badade4d01bff312c56ce3ddb34b0fdde3b0c2706fc292b9fac7e1a0dcd0b6534c968117f7de15eba84d2754e4bcb8093a5440297605598659f686075e2b1b464b6b3ec68abb13cde263b1c607545c45746338b9b207b5c381da690f653b35e363e1249551ad938b9fd7b0a944151cda07127bf9ba76958e926472f4aa1de8512ce834cfcae5414b226f23acdb1fe5cf685d2201b78167ad35fc1da282744c2a43cc49d49242f968f7e06de14455e7ef5adedc5b33184346018114e2d1fc7a5349e378da9b2af5b328c213888652aca9f1145363809eca7c1fd8e64a5cc3255418736e048a731f3053db77971f67014e6121a8e464833e5dbd02ea6caf385e43e9f378bfba657986bf852b32adb55e35a2675bfc8d70d43a902032a61f59f57dad2dd7d7963322136233200cb9a90c952074e9ba0fc0654f1b6fd6f7f0eb77c0fa6d8143213ce6e8b0c178f73e17a7c64839f9bebca2fc955ea8ae406a13b80a9045fa8d129fd859faa46fd27c48bde7b890f98ee938c0d78889f84181ae2f5711304fe554d4251bbc6437ced59d577a2a1f26da736193c3674adb13cef9f4cb4aa6585c4d6874b0309ecde300493b1642c595746f09e03977c8902f3a4a877db1153b248f295a0ca2f1e437d15fcab8fd77c5f967304efb5c4920b990674ae61b954af40be17a8559dc377c591b68067fdcaf2d27bd9a22041b981a84be3de50d5962b58f8c4a22fa05192c5ac99a0a9423284fe62a3a59f085136cec72cda2a53af106a2eb5bda28b6e02c299118cd91714c2e7d045346c78d9ed1b41c73231a21e42c298949f70122277f4134ed5c56639edbf3c3e717310e3d1f03dc5a94e64c4ce148bc5c6bde64eb80b17d5979892786a31225eb89bf9f5a582bcf65b83ff7aa361ccd9238d144f6a22a3f77dd8a01382df4ee90a2057dd310a6b0c4b81dfc92a2cc0c606d3be8b18fbe64ddfdf2004eeabea892be2f914edd1edd8e8829dc7704d71bbaaf08c41824dd0f4b34c9eedead9e10e53bfc6fc0bd37417de0c5c71cff0754d672f29c262d8e27b524427e12bc4e4705ab311d3bedcb1ddd09a3ca0c268c05c64951b7d724a9dafe4d249aaabda91d68633aaab845bf78f9a22d467c7e0c5fc70fc9a318b01d7492efea7fffd329d70692e76647ae665c62b280da0d62f870a52e4dc4cd92c9150c96aab16f8c23475e3152d4debb41b6756f000c3d8aceef18b49e295be7a71da1eeadf4eb96509d45d7cc42af4b7013d8bb445f577e8d4cff92770b8ba0e451f3e24c6d981efdb68c7f2dfafee40b8a425955796e369f0d4da3e998c1626ae0fa583334475f1fdde68ca211c3f2e9afb003f553191702e11f8b731c89ea26059ea4466f2bd0a1a5601025ca9417006bca5c9a57dfdba44c603ef9ad38922623b40feda036d84425c47fa42973e348a180a7570e1215044c375313ab08d6f521052dda415707ebb74d6c4774e039bb04cadc2799224bde1802e2ee2a018032e3a341700c0fa2aa28bf93cc479231efe7da0e9f68e572415348c08cf648117e9b6d1267fef6617f5927252c86cc087775db3e30180feb5ce7e1ac9c3761161e07a4853aa6d97e525aa88302954cf9390fde81f8e11d97a11c79e3bad261364c18890dd1f8fc71127edefe3571518a42be611a46a0426a33221aa25a0ae6514daaf96038cb59aaba898de49e3b215a4464e0af614e638c2d9b6e676ec427fc906bc516331a18121f306a5246d179e2d3d0f38ab8393f7ea5a2d24585e7cca649637b9983924a15483c167e8780f8dd7aa1154cbf731745a8d8d54a8c4f8d854371bb8172303f9ba3c8c7cfe8c378ee56bc35c6376aafe907d3294ee9a8786281b7deff78ff125761f1a31d0e8fffe04a52a7574eeb8679670ca3bfb740167a559488d4337819613d32752d8a89013622f6a8d70f3c64b84a4215f4b7bb282a2d17c36a326167e3270757b8f1d9a0137bfc5ec278e8ca35a69e49779cfc25b95a89cc18732b5b9d1986b18878c57e118506909207207ad0b4edf32fb2b35b6e70546f45d0849bd139ffff9d8ae547787e7b51403b54f110e2ac65468cd0910d80a4e321deafd46e9af19609bee1efa41b762b8ace989dd681503539e7d9948664cf7a73ffac9ce2a34b514253c4f21bbccd38057a6d68732930dcdfc9a32219b53339d100db0037a8bbd101e71f5054f3\nAAD = 7b3b9c07148fcd897f657ecfcc87e530191536b8e77f9309e8d7323888b3b21477f2ab7c885c105d9c29ac96aed23b366f9fde4177401b7038c6770c7bd2ee8b4335105cc0eab9e367f0cea90d6f1ae3fa76cd21ceb9f3500ce7fb4b2a3f9e90f900a231ec693aeced7afb6821391d1f5b1b957895777aa7a2b71d9571c00336f26d54d756392cdb74bfb67d5a621d517db20441f74d0940180baf613b09452f64224f8af7bbc864ab4a8434ff624d0c0646ee07132fd376506951899bde975df8c836ab4ed9cc084f1f6d500ad56345d2f250a0d6991b9e458c62b6023191f341c8659e8a38c878cfac12b032674503df9c9bb01c4340c709eb6dd7c74907d769a317f4dd7317843c47bdb4c5e1f07f2380d464b0c47269389cc8a43a09adba86f6aa8f44c8fe514e73b5fe8d344769c1aa20a4538ecfbf47562ca79fa497b0f02f103f75522db9ead50d56dbe86997d6085f1b5aa7a4cab9e51a1247ce4f724a14983b6bafd17369fac973c6be268e20d800de870928e100990ebb0d3bedfceda36c64be3a729b603bce677a49e8caf282c9159b6e3e1e775129bd30dc3f5c9849535d86a27474be03bb5749b4c0115e2614f8feaa7405cc69b1de479b3b57e551f876a9c8c57ab9879cc68bb2e",
+    "a110b2e77e59dd6a65eaa67cc4d4b2f4d6e646b2a298d3c80fb43969275d4414734e74726145dab06124c040656c39a94846e8fd58d326f4f9eafe5b95d85254765a21993f55070fcb9e85db5d42ab6b9464ce66de3f236dd2a0a26c4e5535dbdcd6eb350209a65aee785c6647ad4103d092a8ac932470880eb314f7c98cdff34fdf35ee2d36f09bd443b5defad7a5acb9df55965421fd043def6f4771e1bb27385b30ba22c0d8972aead6b654085a7dd3b60c4004a0dae22e25100e54e0badd0cadf909799329ddff699de8066dd6c3822d80c73c52d87e6fcbdb2dbbf852e37804b1256e23e76dbe43f30be4a577bc23c7941a3d708d1e1f579e9c6eebc219c74768168f6790a41f883790e08cd1e88ad09a544eb97b3d1d5af67eea666b9c027e5c7c976921189b955a9e605f6cc9c012c1c2e197c5b02504cb9ffbcb0f3ed778d540d5194fdf5d38dba6340c93da7c5501a082689616f337d8b59c2a92c25e777515726e1d7f6cc9552693cc7c30f1294b37f97d49814250d6c1e3eb335c5d214ef3641739d508b87106eaaf367902433a148ca962ec694409acb82d7749e1c88938ad382d0ca6e6cbe8255746832fe737c3e71dae8397f260c98d4a292a126ec21935c24096d2f91ae114194af659455d8a4206197495a28474dd2809debf5f550d77ffac2b0db521559910c352f23472d7aa9f4dbbdb158f40aa36912cbd918ae4c642e76d78d57ade1075c4fe1086ddee3d554353b4693bbcef1cfa87e49890838c36156af0edf384b0413d6d7aa\nTag = 51cbcf4a2fd82f221de1bfebf86a8c24\n\n# OFB tests from OpenSSL upstream.\n\n# OFB-AES128\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = 3B3FD92EB72DAD20333449F8E83CFB4A\n\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = 50FE67CC996D32B6DA0937E99BAFEC60\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = 7789508D16918F03F53C52DAC54ED825\n\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = D9A4DADA0892239F6B8B3D7680E15674\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 9740051E9C5FECF64344F7A82260EDCC\n\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = A78819583F0308E7A6BF36B1386ABF23\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 304C6528F659C77866A510D9C1D6AE5E\n\nCipher = AES-128-OFB\nKey = 2B7E151628AED2A6ABF7158809CF4F3C\nIV = A78819583F0308E7A6BF36B1386ABF23\nPlaintext =\nCiphertext =\n\n\n# OFB-AES192\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = CDC80D6FDDF18CAB34C25909C99A4174\n\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = A609B38DF3B1133DDDFF2718BA09565E\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = FCC28B8D4C63837C09E81700C1100401\n\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 52EF01DA52602FE0975F78AC84BF8A50\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 8D9A9AEAC0F6596F559C6D4DAF59A5F2\n\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = BD5286AC63AABD7EB067AC54B553F71D\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 6D9F200857CA6C3E9CAC524BD9ACC92A\n\nCipher = AES-192-OFB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = BD5286AC63AABD7EB067AC54B553F71D\nPlaintext =\nCiphertext =\n\n\n# OFB-AES256\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = DC7E84BFDA79164B7ECD8486985D3860\n\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = B7BF3A5DF43989DD97F0FA97EBCE2F4A\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = 4FEBDC6740D20B3AC88F6AD82A4FB08D\n\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = E1C656305ED1A7A6563805746FE03EDC\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 71AB47A086E86EEDF39D1C5BBA97C408\n\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 41635BE625B48AFC1666DD42A09D96E7\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 0126141D67F37BE8538F5A8BE740E484\n\nCipher = AES-256-OFB\nKey = 603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4\nIV = 41635BE625B48AFC1666DD42A09D96E7\nPlaintext =\nCiphertext =\n\n\n# AES-192 CBC-mode test from upstream OpenSSL.\nCipher = AES-192-CBC\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 000102030405060708090A0B0C0D0E0F\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = 4F021DB243BC633D7178183A9FA071E8\n\nCipher = AES-192-CBC\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 4F021DB243BC633D7178183A9FA071E8\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = B4D9ADA9AD7DEDF4E5E738763F69145A\n\nCipher = AES-192-CBC\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = B4D9ADA9AD7DEDF4E5E738763F69145A\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = 571B242012FB7AE07FA9BAAC3DF102E0\n\nCipher = AES-192-CBC\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nIV = 571B242012FB7AE07FA9BAAC3DF102E0\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 08B0E27988598881D920A9E64F5615CD\n\n\n# AES-192-ECB tests from FIPS-197\nCipher = AES-192-ECB\nKey = 000102030405060708090A0B0C0D0E0F1011121314151617\nPlaintext = 00112233445566778899AABBCCDDEEFF\nCiphertext = DDA97CA4864CDFE06EAF70A0EC0D7191\n\n\n# AES-192-ECB tests from NIST document SP800-38A\nCipher = AES-192-ECB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nPlaintext = 6BC1BEE22E409F96E93D7E117393172A\nCiphertext = BD334F1D6E45F25FF712A214571FA5CC\n\nCipher = AES-192-ECB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nPlaintext = AE2D8A571E03AC9C9EB76FAC45AF8E51\nCiphertext = 974104846D0AD3AD7734ECB3ECEE4EEF\n\nCipher = AES-192-ECB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nPlaintext = 30C81C46A35CE411E5FBC1191A0A52EF\nCiphertext = EF7AFD2270E2E60ADCE0BA2FACE6444E\n\nCipher = AES-192-ECB\nKey = 8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B\nPlaintext = F69F2445DF4F9B17AD2B417BE66C3710\nCiphertext = 9A4B41BA738D6C72FB16691603C18E0E\n\n# DES ECB tests\n\nCipher = DES-ECB\nKey = 0000000000000000\nPlaintext = 0000000000000000\nCiphertext = 8CA64DE9C1B123A7\n\nCipher = DES-ECB\nKey = FFFFFFFFFFFFFFFF\nPlaintext = FFFFFFFFFFFFFFFF\nCiphertext = 7359B2163E4EDC58\n\nCipher = DES-ECB\nKey = 3000000000000000\nPlaintext = 1000000000000001\nCiphertext = 958E6E627A05557B\n\nCipher = DES-ECB\nKey = 1111111111111111\nPlaintext = 1111111111111111\nCiphertext = F40379AB9E0EC533\n\nCipher = DES-ECB\nKey = 0123456789ABCDEF\nPlaintext = 1111111111111111\nCiphertext = 17668DFC7292532D\n\nCipher = DES-ECB\nKey = 1111111111111111\nPlaintext = 0123456789ABCDEF\nCiphertext = 8A5AE1F81AB8F2DD\n\nCipher = DES-ECB\nKey = FEDCBA9876543210\nPlaintext = 0123456789ABCDEF\nCiphertext = ED39D950FA74BCC4\n\nCipher = DES-ECB\nKey = FEDCBA9876543210\nPlaintext =\nCiphertext =\n",
 };
 static const size_t kLen19 = 455619;
 
@@ -2650,10 +2650,10 @@
     "887d92419e3c839b8b1dc43c6507026f1f851e\nAdditionalInputReseed = 857848c62203307b39728acf11ac8462302d3a41d186778b3f112a86270252f058fbe5767496e47662186b8d0817de02\nAdditionalInput1 = 9a08df0de742fd2e2d55121a58b700dfbff250a4881b02fc3b8952b48ecd4d034d6e7c757cdf91bf7c31dfaf70b1da22\nAdditionalInput2 = 3bf0e4f1291a8bc272cc985878335882c75831510f27963c7c01a879c60c5b67a9a14a656a746a80a091adf6ffb1adf6\nReturnedBits = 69361d61f4cf5ed489888934f320a9acc5383e719f09a1e30b6029bf71d4b4cb54859798ace2d8ee5e681d4acb223b9c119dab2dd07e6db3f7f844c2b46b9c47\n\nEntropyInput = 6f45b55ac62d5ffd452d36b1e4b18cc6abd6ad93e87558b79fbe99b4f4a962b74bad00821019bd126d6f9dd73912acf7\nPersonalizationString = 913a783046baefe428346085fd640caa1874d4aa6974832cacc5b51e78514bcfedd174606bef1721df7a1194a0ccd1e3\nEntropyInputReseed = d9e19ce3197004ab3a4bf995a481149b6d8e59a3970161cb0d3917374c0c86bb5e9bc509bd01b6796fa1e77e5fdddb16\nAdditionalInputReseed = fd31068c90614e04463acdf856b034293a079a816f1c5f3de63b870a9876f7397d2f93bd3f6776b56a78f7178e1fbb87\nAdditionalInput1 = 068e3791b91adb820b27c45a5d8544eed3133486a7d2d0bc503d8abad8b7093f3df214f1e0ac4ff2d347c760b2a605d5\nAdditionalInput2 = 6c55927a349d321d1a2141aeccc3543e9726ffcf3d8fdfe1aed63c61972a213c12ea65d648e476268611e9b08486a648\nReturnedBits = 552b4c4035d964b5eb26e3036445793df67b7321d36e8d2362fe284503b587c961a33b816b40b93d4b006769177c6593c553b6e669076f25a3e2a7214156c249\n\nEntropyInput = 8d492df46257a62e717302992682e28494f84d0f3237c16439efbbd16a94b3356eb7f7c2a0206892045a0d7d36d69f03\nPersonalizationString = b915f3311feadf6676df2389baacccdafc74cf200ed7b99167b33dafa875ce4aac1a61ce54972ec54f9b0901a3b050ee\nEntropyInputReseed = 2e345fe3a471c5066b20f4aaeaaf73921426ac1bc0509e93671535a8fbc016f5967403d6d13b4760491bf973c47a8ed5\nAdditionalInputReseed = 2e8f1f01a7664890feef93152f7b7f05032b4c70c58b5f261ef0a9c2aef23f2a14ee57c3d3465af24289b1c850e52ef8\nAdditionalInput1 = f8c218c996284f757c491cba025fd84cc701f9f83a16f03c314712c2354fee39214ed5994ab24641826bc15ed1bb5f61\nAdditionalInput2 = 146c53dc4af90f26d8c85822810d9bde2949495c23ca2c7c13ceee8221cbe8105491d560e0044c8d50d7e365da41890b\nReturnedBits = b19dd18494235abdcb3b4e99c9355d19d543c7fb7e1048d63d9e25abcc12dcb31549cec2818667713ad1dc35142072ca8daa511927aca71303493e500503be8b\n\nEntropyInput = 2a595f08947d0056ed19ecc8a547867834c125cf1740230a1325b93bf29951cbedcaa6a8f5cbe69801fb9a197ad576c6\nPersonalizationString = c0fb9dbea13863dfbfdb2c0dd864887413794a07a4dd228836f0f6afab901496486a2d508a3f4b784d83382629cddbc1\nEntropyInputReseed = a3ddfc3aefc94dcaf656f4d8eef0065a6d233a76e0a4c26e2d8bbb86459c9e4a173ff8cbec85ebc14712640741427668\nAdditionalInputReseed = d4eb9b12fc2a6fc75a69701c8b5ece02e47f813effb705dbdef0294e38907f5aadf40bdbcc067d0ebc0bf3661a2e0990\nAdditionalInput1 = d3c4bf4f8a3f61413fe1953523ba83bc02ae6d7eea1c8ba288f8e06f0d0276e61667a410f90548d283c4f1ae79483961\nAdditionalInput2 = e6fc29680eef141e1372e17729c9f4407b3f7a57057be1081ce46c78693334bf56867791072d9d18a7b4835bf251712d\nReturnedBits = 08475bfb8e621e12e212f4960e737023f7a3de3b204b6ffeeb929f424ca78c6ae2e0d726237915e5e244616713e88a39df302208e3a324f5444213b70b2e9c09\n\nEntropyInput = 3d5332b91dcaad893df504c7c675b69890b28055dfd3ec7d0ba56990dcda175096ff8f8f9f9ecd6dddeb96b829b98c3e\nPersonalizationString = 34753432b3c6bedc4d2296dffce8a66591b400867f7edf10cee447046f5f767036da9f3f4a02f7b8cba7bdd4365c1fef\nEntropyInputReseed = 1a69af0ff696f9a2fe0fddd26ccf6fae215faf91804461928c884eec13b1dd1501862a738373454e1b449c6026cebceb\nAdditionalInputReseed = dc47e104175f3c9812feed9e143da5cd7e553e3423db5bc8be3b652f577100267ff795804aaf42c3991589e7688a977b\nAdditionalInput1 = e64c0cad3dfc0bf0eba3317cf37b7779b91bf3de3e0ba36d3e16feb0014286596edd364c197847e8732925f971294dfa\nAdditionalInput2 = 1ff3a6bdb7e319a86b35d062cf017ed270c6b5511bc3d63083272e76bf4db9d90e836f18d98f1f79c2e91eeb0bc635e2\nReturnedBits = fdef7e67c3d19f5f74784bdc64d2c23d0a3f027846da2b2afba940fdc2f066d89b63d27e55d3d0a88bddb97e29676894ec8a18268e4bc27d181a867c5a80fe2c\n\nEntropyInput = 156c8c0f44f4a8f17044957b572ed16d54b73a9510d2b260a036ad6ef223f45e124c4ae9dae0fdf5684792da0ff5ea0a\nPersonalizationString = 1472f56d5498564a5b53d2eae27103f41f565d7873b37c37c30472075f1d84d69a8d68c92636ae06aa61678358cc47d4\nEntropyInputReseed = 6bc2bfd44f72982b23b6d22c85f7d601d5941ebe6e9ee9d06a8efe652539ec00b1136adbc4e43aaab76c3ad6a57a267e\nAdditionalInputReseed = a83b1547fadb97e491ef0bdb8b0d006ab16638fdb211613051f0f57586c146882f1062b7f71a8c15bb27200fa83ffcaf\nAdditionalInput1 = ac188da8a895e0807bab61b1ebd73acbe43f99f375c7d29174f675e1667237cedf1d41fc50da277a0db21ee2891a5abf\nAdditionalInput2 = adf9a30b6cd9dac23f193f66df9d606a5765b96cc42c099530c53c26a6d6bcf0779ae060d188e1cc5bdab6c312892267\nReturnedBits = fcc79828bf2c9d3d799d7a0e83583c70aeb156e25df0b2fde030c2775c9986c8054e848128ba38ff5e5b0b1e5b72f9e41726b960008408f5152b1a47072fbdf7\n\nEntropyInput = 5e0dd8e37aa23d0fe79efcb8e5538ee90ffb5733decc83f7677488c4f64c2e0d2a918968a5c7351de9360f23fe606a4a\nPersonalizationString = 5161b794e4a13371b320ae4b2b6b698e4df507402a7582555598089dd9484358d37f891294948512e4198a79de8ca3d9\nEntropyInputReseed = 850ffb89bc23562f3ea03947c1afa7309a0ccdc0b714bc94ff45d66884b74fba0bffce97887f2efc178388f36f0ea95e\nAdditionalInputReseed = 50f605cb940f573c4ef3ddff8caa9a3f544f40fdf583b82cd79338523d896f72f86be1f9bb37d9aca02a2856f713c812\nAdditionalInput1 = da7bcf6810a6b3820548065edbec0fa2fd4bc03e5d50666f98a8bea6b744e04aafa0714582f11b6608852c45ee51df6b\nAdditionalInput2 = 4e9c0c770699656217a0b2dbb466a0fcb0cdb5d4a4a05da40fa2eb546f2f0f28aea575a0bdf6e89352d519db44f47dc5\nReturnedBits = 40ec0e8e3e54cd4fd78f5e006bf3134c378ba619ccf9e7530c79217da631458b5f9135bc8b0d6f2e742c53b58d0ebc18263f9d2ef37a2fa0fb086d2193857863\n\nEntropyInput = 219a364ad362fd8a1d69c1284fd64cc9ec05e6bfbdd133ac9170594b5d95946d3dda2ebcc58deeace86f9dca5ad99c18\nPersonalizationString = b0b27b03fc65effe4610e61916f2e9bd252d47efcc08b84aa505b1befd3a7e9295ca764ac88f099bbecc28301f0f298e\nEntropyInputReseed = 1bccdb4b2ce863fbee104d7a56f2cd88a44a088392883a6db30e6fc7bf5611759c71d53a61fe62b6314d7426e510a722\nAdditionalInputReseed = a06eab3b05eb3649a01be82a356030effdbf45fd71f49b1862ecf33fdb28a7191a34f104b9eda1da4ba48daf9da381b6\nAdditionalInput1 = 88b17e13e02a7e9a3f8263946fae0b6a9052692c3c5cbe858369c4d0b198b9c6a8f4c87ff5e6b2835a7944b911266aa9\nAdditionalInput2 = 828222c8a275427a5f8a963d0d65e92f6170d5089c9a162429c093a28dd69f71135342f16b3baea9a4764e2cc3762267\nReturnedBits = a739f9fee9a40049e42b00b381a1f663877abb776ec655e3a7870bd94bea1a25d3c6d380eced435e498044daf78b349bc1868e4bff6257cf2711ed08e2357201\n\nEntropyInput = f8513c2f6e46f75fce3671ccb3c69158583a873b0dda83d8b1cd548f4e5efd75642f2c23cf8792d51023f31d795f6f5b\nPersonalizationString = e94f7212257885511b15a873d0158fa5ea648846afbcf9d62a7abc4e6909dd43c671fa18bc289f47e2301e9aa69c3e88\nEntropyInputReseed = 487b9c1866ffdaaf6760cdee3df5a930196d30b0ac8db780656577ddfc1ff3508c451ef2b0b478a2cf0c73027597b2ae\nAdditionalInputReseed = 7387b59f8c78e79d36edacbb428fd515ef9e58086059bf7fd642053b0f5706cfe86eb3c35ca0b6f02e5d1304e476e3cf\nAdditionalInput1 = bafc59ef40e59d8c28851212e1c357cce0d06f02c69d14585170ca8eb18396a331d635cf0651319842cee2f7c87285c2\nAdditionalInput2 = 82d905ba69b6c45ca28107beb6698a152631ab48614235f6a9f12e019f66bf326151d9ee84e0cd42b6566dbffb46ec35\nReturnedBits = 163f48d09f3e9ebf9f8647766a0b2e189a2f3a21a5a4e31c8a4f3bd138cc50030ac8bea1c4d9e9aa0b67f34926239fbcedfd1992da165b941cf03b56737737b2\n\nEntropyInput = 0a8f092764d473d48215e947ea8a32b5da8ea86d8ed0d83bce31b1b5a21e7c5f1af2186a39caf9afa61644caef02ef9b\nPersonalizationString = 84637faba756917b0f44ba2e0e26546f5239782e4d9f952ee251367081629e71c20a7459995a7563810daeab74220664\nEntropyInputReseed = eacaded3d89a63b94a5c14c3087306e670fc4ee7d56cafef4c5aa7d553ca89ed34418056a44f5447e2b07dd541ac4645\nAdditionalInputReseed = 104a1541313fc4be9d34bced288c1c1b6fa793e9096f8be5673a2c6825dabef91fd88e45a061b2d897f9b5e8a8ae0ad3\nAdditionalInput1 = e30c9d8aba0bd5dc63d911897e4dcadbeacc3ed9392d8e361b356e02a81d65bdab91f7e9f8dd2b2bf9af0da5100e77d4\nAdditionalInput2 = 2bed42b8c05461ba2756a5f38393d5538d20eb9cf1c06775e7fef7284341f61ccd34b0148d1e870c1992dfd065560ed6\nReturnedBits = 855a6af3e6dddf194ceaafcef6f7ee91f489a61a73b759d41be4d5298510b9902f5b78b1162cba417684519634b578afda9802bd782f04b3a25106f3586ad8a4\n\nEntropyInput =",
     " ed63e3b88a2f0d6a40b7b36a06cd8d40ec10158ab6c4f8dabdb879e45d22db573320d2641bfd7db31eb6bbf4c6330b9f\nPersonalizationString = f82a5ee81f349a69dfa6a6e53b406bb560cb0339f61b3886237023ae4582b9725b8774a8044ec5134ebd4d7606f18188\nEntropyInputReseed = 7104522c2a69bec7e010d9670d7ab92dd817ac7ff05d63ab0e12d431d4c9aef4d715421c1fb5d05d3aecba36859a6038\nAdditionalInputReseed = 3b83034ea440604d0b7d04857623e9adc0eb9aad4a45b2079863eed3d72e65abf67d255673e769a2e2584eea59f1ebaa\nAdditionalInput1 = 8678e4f831887e96a27c3a8ce0963f4b34bbca25791b06526cb6c55624c33d98a0356be624238cecacba3535e872c4c7\nAdditionalInput2 = ec3452ea2722984bbe6a5b7c22ad58df37a854abc8630b9e21bcd0469eb207e5f44e044f5f666920dd55e81a393500de\nReturnedBits = c340765fe7a3479bef2d7d59e321066f8ad0db53aed4c517bf8339566bf877d53921e6de2650d0080529004a5fd32124ce8e58a040e2d55656b37d9ea827cb91\n\nEntropyInput = 882ed05487dce4b4f9e58ec4f2da1fa5d8ebb4ef9fcbf7b0a0e15c4dccb8e19788f86dcf2885e71a20cbd9ac10a6648e\nPersonalizationString = 05f5bc41687ea1e4c34a69944fbae283cfee4c42b1bbd78fddb0973d0fca948539b6843658b67c30b73191b9a0bf2921\nEntropyInputReseed = ca1603d4c8711404c7bdc12c7c75b2943a4b042ea1d2eb54506a6876952157caf3b152dc75f27f2213645a141577e8ba\nAdditionalInputReseed = 83cda53380888d53515e58154f89d5528ab69f31fbcfca34988cf03c4cae5f60aa6291f32d99ab2a726b6e08d2502cf5\nAdditionalInput1 = 5bf5ca9f964edd91e8ef491fd3cd32faf9cb9d1993d8221914d1751fb0d4252a5ca950e213f088050900b2bd74f5e336\nAdditionalInput2 = dba28dc1d8d615651547867d4ef42519045ee16378143685101da47a27b55498078e8a8f4854052f7cc6f5b02e571ae8\nReturnedBits = 01f11971835819c1148aa079eea09fd5b1aa3ac6ba557ae3317b1a33f4505174cf9d7e940821c9b0e5527a1d3e186a7a83f187c62d3223cf5964ff9526d8484c\n",
 };
-static const size_t kLen48 = 6202;
+static const size_t kLen48 = 6201;
 
 static const char *kData48[] = {
-    "HMAC = MD5\n# Note: The empty key results in passing NULL to HMAC_Init_ex, so this tests\n# that HMAC_CTX and HMAC treat NULL as the empty key initially.\nKey =\nInput = \"More text test vectors to stuff up EBCDIC machines :-)\"\nOutput = e9139d1e6ee064ef8cf514fc7dc83e86\n\n# HMAC tests from RFC 2104\nHMAC = MD5\nKey = 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b\nInput = \"Hi There\"\nOutput = 9294727a3638bb1c13f48ef8158bfc9d\n\nHMAC = MD5\nKey = \"Jefe\"\nInput = \"what do ya want for nothing?\"\nOutput = 750c783e6ab0b503eaa86e310a5db738\n\nHMAC = MD5\nKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nInput = DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\nOutput = 56be34521d144c88dbb8c733f0e8b3f6\n\n# HMAC tests from NIST test data\n\nHMAC = SHA1\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F\nOutput = 5FD596EE78D5553C8FF4E72D266DFD192366DA29\n\nHMAC = SHA1\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F10111213\nOutput = 4C99FF0CB1B31BD33F8431DBAF4D17FCD356A807\n\nHMAC = SHA1\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263\nOutput = 2D51B2F7750E410584662E38F133435F4C4FD42A\n\nHMAC = SHA224\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F\nOutput = C7405E3AE058E8CD30B08B4140248581ED174CB34E1224BCC1EFC81B\n\nHMAC = SHA224\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B\nOutput = E3D249A8CFB67EF8B7A169E9A0A599714A2CECBA65999A51BEB8FBBE\n\nHMAC = SHA224\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263\nOutput = 91C52509E5AF8531601AE6230099D90BEF88AAEFB961F4080ABC014D\n\nHMAC = SHA256\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F\nOutput = 8BB9A1DB9806F20DF7F77B82138C7914D174D59E13DC4D0169C9057B133E1D62\n\nHMAC = SHA256\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nOutput = A28CF43130EE696A98F14A37678B56BCFCBDD9E5CF69717FECF5480F0EBDF790\n\nHMAC = SHA256\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263\nOutput = BDCCB6C72DDEADB500AE768386CB38CC41C63DBB0878DDB9C7A38A431B78378D\n\nHMAC = SHA384\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F\nOutput = 63C5DAA5E651847CA897C95814AB830BEDEDC7D25E83EEF9195CD45857A37F448947858F5AF50CC2B1B730DDF29671A9\n\nHMAC = SHA384\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F\nOutput = 6EB242BDBB582CA17BEBFA481B1E23211464D2B7F8C20B9FF2201637B93646AF5AE9AC316E98DB45D9CAE773675EEED0\n\nHMAC = SHA384\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7\nOutput = 5B664436DF69B0CA22551231A3F0A3D5B4F97991713CFA84BFF4D0792EFF96C27DCCBBB6F79B65D548B40E8564CEF594\n\nHMAC = SHA512\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F\nOutput = FC25E240658CA785B7A811A8D3F7B4CA48CFA26A8A366BF2CD1F836B05FCB024BD36853081811D6CEA4216EBAD79DA1CFCB95EA4586B8A0CE356596A55FB1347\n\nHMAC = SHA512\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F\nOutput = FD44C18BDA0BB0A6CE0E82B031BF2818F6539BD56EC00BDC10A8A2D730B3634DE2545D639B0F2CF710D0692C72A1896F1F211C2B922D1A96C392E07E7EA9FEDC\n\nHMAC = SHA512\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7\nOutput = D93EC8D2DE1AD2A9957CB9B83F14E76AD6B5E0CCE285079A127D3B14BCCB7AA7286D4AC0D4CE64215F2BC9E6870B33D97438BE4AAA20CDA5C5A912B48B8E27F3\n\n# Additional HMAC tests from OpenSSL.\nHMAC = SHA1\nInput = \"My test data\"\nKey =\nOutput = 61afdecb95429ef494d61fdee15990cabf0826fc\n\nHMAC = SHA256\nInput = \"My test data\"\nKey =\nOutput = 2274b195d90ce8e03406f4b526a47e0787a88a65479938f1a5baa3ce0f079776\n\nHMAC = SHA256\nInput = \"My test data\"\nKey = \"123456\"\nOutput = bab53058ae861a7f191abe2d0145cbb123776a6369ee3f9d79ce455667e411dd\n\nHMAC = SHA1\nInput = \"My test data\"\nKey = \"12345\"\nOutput = 7dbe8c764c068e3bcd6e6b0fbcd5e6fc197b15bb\n",
+    "HMAC = MD5\n# Note: The empty key results in passing NULL to HMAC_Init_ex, so this tests\n# that HMAC_CTX and HMAC treat NULL as the empty key initially.\nKey =\nInput = \"More text test vectors to stuff up EBCDIC machines :-)\"\nOutput = e9139d1e6ee064ef8cf514fc7dc83e86\n\n# HMAC tests from RFC2104\nHMAC = MD5\nKey = 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b\nInput = \"Hi There\"\nOutput = 9294727a3638bb1c13f48ef8158bfc9d\n\nHMAC = MD5\nKey = \"Jefe\"\nInput = \"what do ya want for nothing?\"\nOutput = 750c783e6ab0b503eaa86e310a5db738\n\nHMAC = MD5\nKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\nInput = DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\nOutput = 56be34521d144c88dbb8c733f0e8b3f6\n\n# HMAC tests from NIST test data\n\nHMAC = SHA1\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F\nOutput = 5FD596EE78D5553C8FF4E72D266DFD192366DA29\n\nHMAC = SHA1\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F10111213\nOutput = 4C99FF0CB1B31BD33F8431DBAF4D17FCD356A807\n\nHMAC = SHA1\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263\nOutput = 2D51B2F7750E410584662E38F133435F4C4FD42A\n\nHMAC = SHA224\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F\nOutput = C7405E3AE058E8CD30B08B4140248581ED174CB34E1224BCC1EFC81B\n\nHMAC = SHA224\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B\nOutput = E3D249A8CFB67EF8B7A169E9A0A599714A2CECBA65999A51BEB8FBBE\n\nHMAC = SHA224\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263\nOutput = 91C52509E5AF8531601AE6230099D90BEF88AAEFB961F4080ABC014D\n\nHMAC = SHA256\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F\nOutput = 8BB9A1DB9806F20DF7F77B82138C7914D174D59E13DC4D0169C9057B133E1D62\n\nHMAC = SHA256\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F\nOutput = A28CF43130EE696A98F14A37678B56BCFCBDD9E5CF69717FECF5480F0EBDF790\n\nHMAC = SHA256\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F60616263\nOutput = BDCCB6C72DDEADB500AE768386CB38CC41C63DBB0878DDB9C7A38A431B78378D\n\nHMAC = SHA384\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F\nOutput = 63C5DAA5E651847CA897C95814AB830BEDEDC7D25E83EEF9195CD45857A37F448947858F5AF50CC2B1B730DDF29671A9\n\nHMAC = SHA384\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F\nOutput = 6EB242BDBB582CA17BEBFA481B1E23211464D2B7F8C20B9FF2201637B93646AF5AE9AC316E98DB45D9CAE773675EEED0\n\nHMAC = SHA384\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7\nOutput = 5B664436DF69B0CA22551231A3F0A3D5B4F97991713CFA84BFF4D0792EFF96C27DCCBBB6F79B65D548B40E8564CEF594\n\nHMAC = SHA512\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F\nOutput = FC25E240658CA785B7A811A8D3F7B4CA48CFA26A8A366BF2CD1F836B05FCB024BD36853081811D6CEA4216EBAD79DA1CFCB95EA4586B8A0CE356596A55FB1347\n\nHMAC = SHA512\nInput = \"Sample message for keylen<blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F\nOutput = FD44C18BDA0BB0A6CE0E82B031BF2818F6539BD56EC00BDC10A8A2D730B3634DE2545D639B0F2CF710D0692C72A1896F1F211C2B922D1A96C392E07E7EA9FEDC\n\nHMAC = SHA512\nInput = \"Sample message for keylen=blocklen\"\nKey = 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7\nOutput = D93EC8D2DE1AD2A9957CB9B83F14E76AD6B5E0CCE285079A127D3B14BCCB7AA7286D4AC0D4CE64215F2BC9E6870B33D97438BE4AAA20CDA5C5A912B48B8E27F3\n\n# Additional HMAC tests from OpenSSL.\nHMAC = SHA1\nInput = \"My test data\"\nKey =\nOutput = 61afdecb95429ef494d61fdee15990cabf0826fc\n\nHMAC = SHA256\nInput = \"My test data\"\nKey =\nOutput = 2274b195d90ce8e03406f4b526a47e0787a88a65479938f1a5baa3ce0f079776\n\nHMAC = SHA256\nInput = \"My test data\"\nKey = \"123456\"\nOutput = bab53058ae861a7f191abe2d0145cbb123776a6369ee3f9d79ce455667e411dd\n\nHMAC = SHA1\nInput = \"My test data\"\nKey = \"12345\"\nOutput = 7dbe8c764c068e3bcd6e6b0fbcd5e6fc197b15bb\n",
 };
 static const size_t kLen49 = 170135;
 
diff --git a/err_data.c b/err_data.c
index 4f4e973..7739890 100644
--- a/err_data.c
+++ b/err_data.c
@@ -193,47 +193,47 @@
     0x283480b9,
     0x283500f7,
     0x28358c79,
-    0x2c32326b,
+    0x2c323234,
     0x2c329313,
-    0x2c333279,
-    0x2c33b28b,
-    0x2c34329f,
-    0x2c34b2b1,
-    0x2c3532cc,
-    0x2c35b2de,
-    0x2c36330e,
+    0x2c333242,
+    0x2c33b254,
+    0x2c343268,
+    0x2c34b27a,
+    0x2c353295,
+    0x2c35b2a7,
+    0x2c3632d7,
     0x2c36833a,
-    0x2c37331b,
-    0x2c37b347,
-    0x2c38336c,
-    0x2c38b383,
-    0x2c3933a1,
-    0x2c39b3b1,
-    0x2c3a33c3,
-    0x2c3ab3d7,
-    0x2c3b33e8,
-    0x2c3bb407,
+    0x2c3732e4,
+    0x2c37b310,
+    0x2c383335,
+    0x2c38b34c,
+    0x2c39336a,
+    0x2c39b37a,
+    0x2c3a338c,
+    0x2c3ab3a0,
+    0x2c3b33b1,
+    0x2c3bb3d0,
     0x2c3c1325,
     0x2c3c933b,
-    0x2c3d341b,
+    0x2c3d33e4,
     0x2c3d9354,
-    0x2c3e3438,
-    0x2c3eb446,
-    0x2c3f345e,
-    0x2c3fb476,
-    0x2c4034a0,
+    0x2c3e3401,
+    0x2c3eb40f,
+    0x2c3f3427,
+    0x2c3fb43f,
+    0x2c403469,
     0x2c409226,
-    0x2c4134b1,
-    0x2c41b4c4,
+    0x2c41347a,
+    0x2c41b48d,
     0x2c4211ec,
-    0x2c42b4d5,
+    0x2c42b49e,
     0x2c43072f,
-    0x2c43b3f9,
-    0x2c44335a,
-    0x2c44b483,
-    0x2c4532f1,
-    0x2c45b32d,
-    0x2c463391,
+    0x2c43b3c2,
+    0x2c443323,
+    0x2c44b44c,
+    0x2c4532ba,
+    0x2c45b2f6,
+    0x2c46335a,
     0x30320000,
     0x30328015,
     0x3033001f,
@@ -427,203 +427,201 @@
     0x404da01b,
     0x404e202f,
     0x404ea03c,
-    0x404f20d6,
-    0x404fa14c,
-    0x405021a3,
-    0x4050a1b7,
-    0x405121ea,
-    0x405221fa,
-    0x4052a21e,
-    0x40532236,
-    0x4053a249,
-    0x4054225e,
-    0x4054a281,
-    0x405522ac,
-    0x4055a2e9,
-    0x4056230e,
-    0x4056a327,
-    0x4057233f,
-    0x4057a352,
-    0x40582367,
-    0x4058a38e,
-    0x405923bd,
-    0x4059a3ea,
-    0x405a23fe,
-    0x405aa40e,
-    0x405b2426,
-    0x405ba437,
-    0x405c244a,
-    0x405ca489,
-    0x405d2496,
-    0x405da4bb,
-    0x405e24f9,
+    0x404f20b9,
+    0x404fa12f,
+    0x40502186,
+    0x4050a19a,
+    0x405121cd,
+    0x405221dd,
+    0x4052a201,
+    0x40532219,
+    0x4053a22c,
+    0x40542241,
+    0x4054a264,
+    0x4055228f,
+    0x4055a2cc,
+    0x405622f1,
+    0x4056a30a,
+    0x40572322,
+    0x4057a335,
+    0x4058234a,
+    0x4058a371,
+    0x405923a0,
+    0x4059a3cd,
+    0x405a23e1,
+    0x405aa3f1,
+    0x405b2409,
+    0x405ba41a,
+    0x405c242d,
+    0x405ca46c,
+    0x405d2479,
+    0x405da49e,
+    0x405e24dc,
     0x405e8ac0,
-    0x405f2534,
-    0x405fa541,
-    0x4060254f,
-    0x4060a571,
-    0x406125d2,
-    0x4061a60a,
-    0x40622621,
-    0x4062a632,
-    0x4063267f,
-    0x4063a694,
-    0x406426ab,
-    0x4064a6d7,
-    0x406526f2,
-    0x4065a709,
-    0x40662721,
-    0x4066a74b,
-    0x40672776,
-    0x4067a7bb,
-    0x40682803,
-    0x4068a824,
-    0x40692856,
-    0x4069a884,
-    0x406a28a5,
-    0x406aa8c5,
-    0x406b2a4d,
-    0x406baa70,
-    0x406c2a86,
-    0x406cad90,
-    0x406d2dbf,
-    0x406dade7,
-    0x406e2e15,
-    0x406eae62,
-    0x406f2ebb,
-    0x406faef3,
-    0x40702f06,
-    0x4070af23,
+    0x405f24fd,
+    0x405fa50a,
+    0x40602518,
+    0x4060a53a,
+    0x4061259b,
+    0x4061a5d3,
+    0x406225ea,
+    0x4062a5fb,
+    0x40632648,
+    0x4063a65d,
+    0x40642674,
+    0x4064a6a0,
+    0x406526bb,
+    0x4065a6d2,
+    0x406626ea,
+    0x4066a714,
+    0x4067273f,
+    0x4067a784,
+    0x406827cc,
+    0x4068a7ed,
+    0x4069281f,
+    0x4069a84d,
+    0x406a286e,
+    0x406aa88e,
+    0x406b2a16,
+    0x406baa39,
+    0x406c2a4f,
+    0x406cad59,
+    0x406d2d88,
+    0x406dadb0,
+    0x406e2dde,
+    0x406eae2b,
+    0x406f2e84,
+    0x406faebc,
+    0x40702ecf,
+    0x4070aeec,
     0x4071080f,
-    0x4071af35,
-    0x40722f48,
-    0x4072af7e,
-    0x40732f96,
+    0x4071aefe,
+    0x40722f11,
+    0x4072af47,
+    0x40732f5f,
     0x40739525,
-    0x40742faa,
-    0x4074afc4,
-    0x40752fd5,
-    0x4075afe9,
-    0x40762ff7,
+    0x40742f73,
+    0x4074af8d,
+    0x40752f9e,
+    0x4075afb2,
+    0x40762fc0,
     0x407692e9,
-    0x4077301c,
-    0x4077b05c,
-    0x40783077,
-    0x4078b0b0,
-    0x407930c7,
-    0x4079b0dd,
-    0x407a3109,
-    0x407ab11c,
-    0x407b3131,
-    0x407bb143,
-    0x407c3174,
-    0x407cb17d,
-    0x407d283f,
-    0x407da15c,
-    0x407e308c,
-    0x407ea39e,
+    0x40772fe5,
+    0x4077b025,
+    0x40783040,
+    0x4078b079,
+    0x40793090,
+    0x4079b0a6,
+    0x407a30d2,
+    0x407ab0e5,
+    0x407b30fa,
+    0x407bb10c,
+    0x407c313d,
+    0x407cb146,
+    0x407d2808,
+    0x407da13f,
+    0x407e3055,
+    0x407ea381,
     0x407f1db0,
     0x407f9f83,
-    0x408020e6,
+    0x408020c9,
     0x40809dd8,
-    0x4081220c,
-    0x4081a08a,
-    0x40822e00,
+    0x408121ef,
+    0x4081a06d,
+    0x40822dc9,
     0x40829b2b,
-    0x40832379,
-    0x4083a6bc,
+    0x4083235c,
+    0x4083a685,
     0x40841dec,
-    0x4084a3d6,
-    0x4085245b,
-    0x4085a599,
-    0x408624db,
-    0x4086a176,
-    0x40872e46,
-    0x4087a5e7,
+    0x4084a3b9,
+    0x4085243e,
+    0x4085a562,
+    0x408624be,
+    0x4086a159,
+    0x40872e0f,
+    0x4087a5b0,
     0x40881b69,
-    0x4088a7ce,
+    0x4088a797,
     0x40891bb8,
     0x40899b45,
-    0x408a2abe,
+    0x408a2a87,
     0x408a993d,
-    0x408b3158,
-    0x408baed0,
-    0x408c246b,
+    0x408b3121,
+    0x408bae99,
+    0x408c244e,
     0x408c9975,
     0x408d1ed4,
     0x408d9e1e,
     0x408e2004,
-    0x408ea2c9,
-    0x408f27e2,
-    0x408fa5b5,
-    0x40902797,
-    0x4090a4ad,
-    0x40912aa6,
+    0x408ea2ac,
+    0x408f27ab,
+    0x408fa57e,
+    0x40902760,
+    0x4090a490,
+    0x40912a6f,
     0x4091999b,
     0x40921c05,
-    0x4092ae81,
-    0x40932f61,
-    0x4093a187,
+    0x4092ae4a,
+    0x40932f2a,
+    0x4093a16a,
     0x40941e00,
-    0x4094aad7,
-    0x40952643,
-    0x4095b0e9,
-    0x40962e2d,
-    0x4096a0ff,
-    0x409721d2,
+    0x4094aaa0,
+    0x4095260c,
+    0x4095b0b2,
+    0x40962df6,
+    0x4096a0e2,
+    0x409721b5,
     0x4097a053,
     0x40981c65,
-    0x4098a657,
-    0x40992e9d,
-    0x4099a2f6,
-    0x409a228f,
+    0x4098a620,
+    0x40992e66,
+    0x4099a2d9,
+    0x409a2272,
     0x409a9959,
     0x409b1e5a,
     0x409b9e85,
-    0x409c303e,
+    0x409c3007,
     0x409c9ead,
-    0x409d20bb,
-    0x409da0a0,
+    0x409d209e,
+    0x409da083,
     0x409e1cf6,
-    0x409ea134,
-    0x409f211c,
+    0x409ea117,
+    0x409f20ff,
     0x409f9e4d,
-    0x40a0251a,
-    0x40a0a06d,
-    0x41f42978,
-    0x41f92a0a,
-    0x41fe28fd,
-    0x41feabb3,
-    0x41ff2ce1,
-    0x42032991,
-    0x420829b3,
-    0x4208a9ef,
-    0x420928e1,
-    0x4209aa29,
-    0x420a2938,
-    0x420aa918,
-    0x420b2958,
-    0x420ba9d1,
-    0x420c2cfd,
-    0x420caae7,
-    0x420d2b9a,
-    0x420dabd1,
-    0x42122c04,
-    0x42172cc4,
-    0x4217ac46,
-    0x421c2c68,
-    0x421f2c23,
-    0x42212d75,
-    0x42262ca7,
-    0x422b2d53,
-    0x422bab75,
-    0x422c2d35,
-    0x422cab28,
-    0x422d2b01,
-    0x422dad14,
-    0x422e2b54,
-    0x42302c83,
-    0x4230abeb,
+    0x41f42941,
+    0x41f929d3,
+    0x41fe28c6,
+    0x41feab7c,
+    0x41ff2caa,
+    0x4203295a,
+    0x4208297c,
+    0x4208a9b8,
+    0x420928aa,
+    0x4209a9f2,
+    0x420a2901,
+    0x420aa8e1,
+    0x420b2921,
+    0x420ba99a,
+    0x420c2cc6,
+    0x420caab0,
+    0x420d2b63,
+    0x420dab9a,
+    0x42122bcd,
+    0x42172c8d,
+    0x4217ac0f,
+    0x421c2c31,
+    0x421f2bec,
+    0x42212d3e,
+    0x42262c70,
+    0x422b2d1c,
+    0x422bab3e,
+    0x422c2cfe,
+    0x422caaf1,
+    0x422d2aca,
+    0x422dacdd,
+    0x422e2b1d,
+    0x42302c4c,
+    0x4230abb4,
     0x4432073a,
     0x44328749,
     0x44330755,
@@ -678,70 +676,69 @@
     0x4c411582,
     0x4c419405,
     0x4c42156e,
-    0x503234e7,
-    0x5032b4f6,
-    0x50333501,
-    0x5033b511,
-    0x5034352a,
-    0x5034b544,
-    0x50353552,
-    0x5035b568,
-    0x5036357a,
-    0x5036b590,
-    0x503735a9,
-    0x5037b5bc,
-    0x503835d4,
-    0x5038b5e5,
-    0x503935fa,
-    0x5039b60e,
-    0x503a362e,
-    0x503ab644,
-    0x503b365c,
-    0x503bb66e,
-    0x503c368a,
-    0x503cb6a1,
-    0x503d36ba,
-    0x503db6d0,
-    0x503e36dd,
-    0x503eb6f3,
-    0x503f3705,
+    0x503234b0,
+    0x5032b4bf,
+    0x503334ca,
+    0x5033b4da,
+    0x503434f3,
+    0x5034b50d,
+    0x5035351b,
+    0x5035b531,
+    0x50363543,
+    0x5036b559,
+    0x50373572,
+    0x5037b585,
+    0x5038359d,
+    0x5038b5ae,
+    0x503935c3,
+    0x5039b5d7,
+    0x503a35f7,
+    0x503ab60d,
+    0x503b3625,
+    0x503bb637,
+    0x503c3653,
+    0x503cb66a,
+    0x503d3683,
+    0x503db699,
+    0x503e36a6,
+    0x503eb6bc,
+    0x503f36ce,
     0x503f8388,
-    0x50403718,
-    0x5040b728,
-    0x50413742,
-    0x5041b751,
-    0x5042376b,
-    0x5042b788,
-    0x50433798,
-    0x5043b7a8,
-    0x504437c5,
+    0x504036e1,
+    0x5040b6f1,
+    0x5041370b,
+    0x5041b71a,
+    0x50423734,
+    0x5042b751,
+    0x50433761,
+    0x5043b771,
+    0x50443780,
     0x5044843e,
-    0x504537d9,
-    0x5045b7f7,
-    0x5046380a,
-    0x5046b820,
-    0x50473832,
-    0x5047b847,
-    0x5048386d,
-    0x5048b87b,
-    0x5049388e,
-    0x5049b8a3,
-    0x504a38b9,
-    0x504ab8c9,
-    0x504b38e9,
-    0x504bb8fc,
-    0x504c391f,
-    0x504cb94d,
-    0x504d395f,
-    0x504db97c,
-    0x504e3997,
-    0x504eb9b3,
-    0x504f39c5,
-    0x504fb9dc,
-    0x505039eb,
+    0x50453794,
+    0x5045b7b2,
+    0x504637c5,
+    0x5046b7db,
+    0x504737ed,
+    0x5047b802,
+    0x50483828,
+    0x5048b836,
+    0x50493849,
+    0x5049b85e,
+    0x504a3874,
+    0x504ab884,
+    0x504b38a4,
+    0x504bb8b7,
+    0x504c38da,
+    0x504cb908,
+    0x504d391a,
+    0x504db937,
+    0x504e3952,
+    0x504eb96e,
+    0x504f3980,
+    0x504fb997,
+    0x505039a6,
     0x505086fe,
-    0x505139fe,
-    0x5051b7b7,
+    0x505139b9,
     0x58320f72,
     0x68320f34,
     0x68328c8c,
@@ -785,19 +782,19 @@
     0x7c321202,
     0x80321418,
     0x80328090,
-    0x8033323a,
+    0x80333203,
     0x803380b9,
-    0x80343249,
-    0x8034b1b1,
-    0x803531cf,
-    0x8035b25d,
-    0x80363211,
-    0x8036b1c0,
-    0x80373203,
-    0x8037b19e,
-    0x80383224,
-    0x8038b1e0,
-    0x803931f5,
+    0x80343212,
+    0x8034b17a,
+    0x80353198,
+    0x8035b226,
+    0x803631da,
+    0x8036b189,
+    0x803731cc,
+    0x8037b167,
+    0x803831ed,
+    0x8038b1a9,
+    0x803931be,
 };
 
 const size_t kOpenSSLReasonValuesLen = sizeof(kOpenSSLReasonValues) / sizeof(kOpenSSLReasonValues[0]);
@@ -1210,7 +1207,6 @@
     "HTTP_REQUEST\0"
     "INAPPROPRIATE_FALLBACK\0"
     "INCONSISTENT_CLIENT_HELLO\0"
-    "INCONSISTENT_ECH_NEGOTIATION\0"
     "INVALID_ALPN_PROTOCOL\0"
     "INVALID_ALPN_PROTOCOL_LIST\0"
     "INVALID_CLIENT_HELLO_INNER\0"
@@ -1263,7 +1259,6 @@
     "OLD_SESSION_CIPHER_NOT_RETURNED\0"
     "OLD_SESSION_PRF_HASH_MISMATCH\0"
     "OLD_SESSION_VERSION_NOT_RETURNED\0"
-    "OUTER_EXTENSION_NOT_FOUND\0"
     "PARSE_TLSEXT\0"
     "PATH_TOO_LONG\0"
     "PEER_DID_NOT_RETURN_A_CERTIFICATE\0"
@@ -1459,7 +1454,6 @@
     "INVALID_PURPOSE\0"
     "INVALID_SECTION\0"
     "INVALID_SYNTAX\0"
-    "INVALID_VALUE\0"
     "ISSUER_DECODE_ERROR\0"
     "NEED_ORGANIZATION_AND_NUMBERS\0"
     "NO_CONFIG_DATABASE\0"
diff --git a/eureka.mk b/eureka.mk
index 6873ad9..d3f84c4 100644
--- a/eureka.mk
+++ b/eureka.mk
@@ -28,7 +28,6 @@
   src/crypto/asn1/a_object.c\
   src/crypto/asn1/a_octet.c\
   src/crypto/asn1/a_print.c\
-  src/crypto/asn1/a_strex.c\
   src/crypto/asn1/a_strnid.c\
   src/crypto/asn1/a_time.c\
   src/crypto/asn1/a_type.c\
@@ -169,13 +168,13 @@
   src/crypto/trust_token/voprf.c\
   src/crypto/x509/a_digest.c\
   src/crypto/x509/a_sign.c\
+  src/crypto/x509/a_strex.c\
   src/crypto/x509/a_verify.c\
   src/crypto/x509/algorithm.c\
   src/crypto/x509/asn1_gen.c\
   src/crypto/x509/by_dir.c\
   src/crypto/x509/by_file.c\
   src/crypto/x509/i2d_pr.c\
-  src/crypto/x509/name_print.c\
   src/crypto/x509/rsa_pss.c\
   src/crypto/x509/t_crl.c\
   src/crypto/x509/t_req.c\
diff --git a/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index dcef3c5..566330f 100644
--- a/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -14,7 +14,6 @@
 #endif
 #include <openssl/arm_arch.h>
 
-#if __ARM_MAX_ARCH__>=7
 .text
 
 .globl	_gcm_init_v8
@@ -65,48 +64,8 @@
 	ext	v17.16b,v22.16b,v22.16b,#8		//Karatsuba pre-processing
 	eor	v17.16b,v17.16b,v22.16b
 	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v21.2d,v22.2d},[x0],#32	//store Htable[1..2]
-	//calculate H^3 and H^4
-	pmull	v0.1q,v20.1d, v22.1d
-	pmull	v5.1q,v22.1d,v22.1d
-	pmull2	v2.1q,v20.2d, v22.2d
-	pmull2	v7.1q,v22.2d,v22.2d
-	pmull	v1.1q,v16.1d,v17.1d
-	pmull	v6.1q,v17.1d,v17.1d
+	st1	{v21.2d,v22.2d},[x0]		//store Htable[1..2]
 
-	ext	v16.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	ext	v17.16b,v5.16b,v7.16b,#8
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v16.16b
-	eor	v4.16b,v5.16b,v7.16b
-	eor	v6.16b,v6.16b,v17.16b
-	eor	v1.16b,v1.16b,v18.16b
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase
-	eor	v6.16b,v6.16b,v4.16b
-	pmull	v4.1q,v5.1d,v19.1d
-
-	ins	v2.d[0],v1.d[1]
-	ins	v7.d[0],v6.d[1]
-	ins	v1.d[1],v0.d[0]
-	ins	v6.d[1],v5.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-	eor	v5.16b,v6.16b,v4.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase
-	ext	v4.16b,v5.16b,v5.16b,#8
-	pmull	v0.1q,v0.1d,v19.1d
-	pmull	v5.1q,v5.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v4.16b,v4.16b,v7.16b
-	eor	v20.16b, v0.16b,v18.16b		//H^3
-	eor	v22.16b,v5.16b,v4.16b		//H^4
-
-	ext	v16.16b,v20.16b, v20.16b,#8		//Karatsuba pre-processing
-	ext	v17.16b,v22.16b,v22.16b,#8
-	eor	v16.16b,v16.16b,v20.16b
-	eor	v17.16b,v17.16b,v22.16b
-	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v20.2d,v21.2d,v22.2d},[x0]		//store Htable[3..5]
 	ret
 
 .globl	_gcm_gmult_v8
@@ -158,8 +117,6 @@
 .align	4
 _gcm_ghash_v8:
 	AARCH64_VALID_CALL_TARGET
-	cmp	x3,#64
-	b.hs	Lgcm_ghash_v8_4x
 	ld1	{v0.2d},[x0]		//load [rotated] Xi
 						//"[rotated]" means that
 						//loaded value would have
@@ -286,288 +243,7 @@
 
 	ret
 
-
-.align	4
-gcm_ghash_v8_4x:
-Lgcm_ghash_v8_4x:
-	ld1	{v0.2d},[x0]		//load [rotated] Xi
-	ld1	{v20.2d,v21.2d,v22.2d},[x1],#48	//load twisted H, ..., H^2
-	movi	v19.16b,#0xe1
-	ld1	{v26.2d,v27.2d,v28.2d},[x1]	//load twisted H^3, ..., H^4
-	shl	v19.2d,v19.2d,#57		//compose 0xc2.0 constant
-
-	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
-#ifndef __ARMEB__
-	rev64	v0.16b,v0.16b
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v7.16b,v7.16b
-	rev64	v4.16b,v4.16b
-#endif
-	ext	v25.16b,v7.16b,v7.16b,#8
-	ext	v24.16b,v6.16b,v6.16b,#8
-	ext	v23.16b,v5.16b,v5.16b,#8
-
-	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
-	eor	v7.16b,v7.16b,v25.16b
-	pmull2	v31.1q,v20.2d,v25.2d
-	pmull	v30.1q,v21.1d,v7.1d
-
-	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-	pmull2	v24.1q,v22.2d,v24.2d
-	pmull2	v6.1q,v21.2d,v6.2d
-
-	eor	v29.16b,v29.16b,v16.16b
-	eor	v31.16b,v31.16b,v24.16b
-	eor	v30.16b,v30.16b,v6.16b
-
-	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	pmull2	v23.1q,v26.2d,v23.2d
-	pmull	v5.1q,v27.1d,v5.1d
-
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	eor	v30.16b,v30.16b,v5.16b
-
-	subs	x3,x3,#128
-	b.lo	Ltail4x
-
-	b	Loop4x
-
-.align	4
-Loop4x:
-	eor	v16.16b,v4.16b,v0.16b
-	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
-	ext	v3.16b,v16.16b,v16.16b,#8
-#ifndef __ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v7.16b,v7.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v28.2d,v3.2d
-	ext	v25.16b,v7.16b,v7.16b,#8
-	pmull2	v1.1q,v27.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	ext	v24.16b,v6.16b,v6.16b,#8
-	eor	v1.16b,v1.16b,v30.16b
-	ext	v23.16b,v5.16b,v5.16b,#8
-
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
-	eor	v7.16b,v7.16b,v25.16b
-	eor	v1.16b,v1.16b,v17.16b
-	pmull2	v31.1q,v20.2d,v25.2d
-	eor	v1.16b,v1.16b,v18.16b
-	pmull	v30.1q,v21.1d,v7.1d
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-	pmull2	v24.1q,v22.2d,v24.2d
-	eor	v0.16b,v1.16b,v18.16b
-	pmull2	v6.1q,v21.2d,v6.2d
-
-	eor	v29.16b,v29.16b,v16.16b
-	eor	v31.16b,v31.16b,v24.16b
-	eor	v30.16b,v30.16b,v6.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	eor	v18.16b,v18.16b,v2.16b
-	pmull2	v23.1q,v26.2d,v23.2d
-	pmull	v5.1q,v27.1d,v5.1d
-
-	eor	v0.16b,v0.16b,v18.16b
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-	eor	v30.16b,v30.16b,v5.16b
-
-	subs	x3,x3,#64
-	b.hs	Loop4x
-
-Ltail4x:
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v28.2d,v3.2d
-	pmull2	v1.1q,v27.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-
-	adds	x3,x3,#64
-	b.eq	Ldone4x
-
-	cmp	x3,#32
-	b.lo	Lone
-	b.eq	Ltwo
-Lthree:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d,v5.2d,v6.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	ext	v24.16b,v6.16b,v6.16b,#8
-	ext	v23.16b,v5.16b,v5.16b,#8
-	eor	v0.16b,v1.16b,v18.16b
-
-	pmull	v29.1q,v20.1d,v24.1d		//H·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	pmull2	v31.1q,v20.2d,v24.2d
-	pmull	v30.1q,v21.1d,v6.1d
-	eor	v0.16b,v0.16b,v18.16b
-	pmull	v7.1q,v22.1d,v23.1d		//H^2·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	pmull2	v23.1q,v22.2d,v23.2d
-	eor	v16.16b,v4.16b,v0.16b
-	pmull2	v5.1q,v21.2d,v5.2d
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	eor	v30.16b,v30.16b,v5.16b
-
-	pmull	v0.1q,v26.1d,v3.1d		//H^3·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v26.2d,v3.2d
-	pmull	v1.1q,v27.1d,v16.1d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-	b	Ldone4x
-
-.align	4
-Ltwo:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d,v5.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	ext	v23.16b,v5.16b,v5.16b,#8
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	pmull	v29.1q,v20.1d,v23.1d		//H·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull2	v31.1q,v20.2d,v23.2d
-	pmull	v30.1q,v21.1d,v5.1d
-
-	pmull	v0.1q,v22.1d,v3.1d		//H^2·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v22.2d,v3.2d
-	pmull2	v1.1q,v21.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-	b	Ldone4x
-
-.align	4
-Lone:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull	v0.1q,v20.1d,v3.1d
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v20.2d,v3.2d
-	pmull	v1.1q,v21.1d,v16.1d
-
-Ldone4x:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	eor	v1.16b,v1.16b,v18.16b
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-#ifndef __ARMEB__
-	rev64	v0.16b,v0.16b
-#endif
-	st1	{v0.2d},[x0]		//write out Xi
-
-	ret
-
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
-#endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/ghashv8-armx32.S b/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
index dcac580..4a7497f 100644
--- a/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
+++ b/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
@@ -14,7 +14,6 @@
 #endif
 #include <openssl/arm_arch.h>
 
-#if __ARM_MAX_ARCH__>=7
 .text
 
 .code	32
@@ -69,7 +68,8 @@
 	vext.8	q9,q14,q14,#8		@ Karatsuba pre-processing
 	veor	q9,q9,q14
 	vext.8	q13,q8,q9,#8		@ pack Karatsuba pre-processed
-	vst1.64	{q13,q14},[r0]!	@ store Htable[1..2]
+	vst1.64	{q13,q14},[r0]		@ store Htable[1..2]
+
 	bx	lr
 
 .globl	_gcm_gmult_v8
@@ -256,5 +256,4 @@
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
-#endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index 9480a38..62e5884 100644
--- a/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -15,7 +15,6 @@
 #endif
 #include <openssl/arm_arch.h>
 
-#if __ARM_MAX_ARCH__>=7
 .text
 .arch	armv8-a+crypto
 .globl	gcm_init_v8
@@ -66,48 +65,8 @@
 	ext	v17.16b,v22.16b,v22.16b,#8		//Karatsuba pre-processing
 	eor	v17.16b,v17.16b,v22.16b
 	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v21.2d,v22.2d},[x0],#32	//store Htable[1..2]
-	//calculate H^3 and H^4
-	pmull	v0.1q,v20.1d, v22.1d
-	pmull	v5.1q,v22.1d,v22.1d
-	pmull2	v2.1q,v20.2d, v22.2d
-	pmull2	v7.1q,v22.2d,v22.2d
-	pmull	v1.1q,v16.1d,v17.1d
-	pmull	v6.1q,v17.1d,v17.1d
+	st1	{v21.2d,v22.2d},[x0]		//store Htable[1..2]
 
-	ext	v16.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	ext	v17.16b,v5.16b,v7.16b,#8
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v16.16b
-	eor	v4.16b,v5.16b,v7.16b
-	eor	v6.16b,v6.16b,v17.16b
-	eor	v1.16b,v1.16b,v18.16b
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase
-	eor	v6.16b,v6.16b,v4.16b
-	pmull	v4.1q,v5.1d,v19.1d
-
-	ins	v2.d[0],v1.d[1]
-	ins	v7.d[0],v6.d[1]
-	ins	v1.d[1],v0.d[0]
-	ins	v6.d[1],v5.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-	eor	v5.16b,v6.16b,v4.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase
-	ext	v4.16b,v5.16b,v5.16b,#8
-	pmull	v0.1q,v0.1d,v19.1d
-	pmull	v5.1q,v5.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v4.16b,v4.16b,v7.16b
-	eor	v20.16b, v0.16b,v18.16b		//H^3
-	eor	v22.16b,v5.16b,v4.16b		//H^4
-
-	ext	v16.16b,v20.16b, v20.16b,#8		//Karatsuba pre-processing
-	ext	v17.16b,v22.16b,v22.16b,#8
-	eor	v16.16b,v16.16b,v20.16b
-	eor	v17.16b,v17.16b,v22.16b
-	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v20.2d,v21.2d,v22.2d},[x0]		//store Htable[3..5]
 	ret
 .size	gcm_init_v8,.-gcm_init_v8
 .globl	gcm_gmult_v8
@@ -159,8 +118,6 @@
 .align	4
 gcm_ghash_v8:
 	AARCH64_VALID_CALL_TARGET
-	cmp	x3,#64
-	b.hs	.Lgcm_ghash_v8_4x
 	ld1	{v0.2d},[x0]		//load [rotated] Xi
 						//"[rotated]" means that
 						//loaded value would have
@@ -287,290 +244,9 @@
 
 	ret
 .size	gcm_ghash_v8,.-gcm_ghash_v8
-.type	gcm_ghash_v8_4x,%function
-.align	4
-gcm_ghash_v8_4x:
-.Lgcm_ghash_v8_4x:
-	ld1	{v0.2d},[x0]		//load [rotated] Xi
-	ld1	{v20.2d,v21.2d,v22.2d},[x1],#48	//load twisted H, ..., H^2
-	movi	v19.16b,#0xe1
-	ld1	{v26.2d,v27.2d,v28.2d},[x1]	//load twisted H^3, ..., H^4
-	shl	v19.2d,v19.2d,#57		//compose 0xc2.0 constant
-
-	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
-#ifndef __ARMEB__
-	rev64	v0.16b,v0.16b
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v7.16b,v7.16b
-	rev64	v4.16b,v4.16b
-#endif
-	ext	v25.16b,v7.16b,v7.16b,#8
-	ext	v24.16b,v6.16b,v6.16b,#8
-	ext	v23.16b,v5.16b,v5.16b,#8
-
-	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
-	eor	v7.16b,v7.16b,v25.16b
-	pmull2	v31.1q,v20.2d,v25.2d
-	pmull	v30.1q,v21.1d,v7.1d
-
-	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-	pmull2	v24.1q,v22.2d,v24.2d
-	pmull2	v6.1q,v21.2d,v6.2d
-
-	eor	v29.16b,v29.16b,v16.16b
-	eor	v31.16b,v31.16b,v24.16b
-	eor	v30.16b,v30.16b,v6.16b
-
-	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	pmull2	v23.1q,v26.2d,v23.2d
-	pmull	v5.1q,v27.1d,v5.1d
-
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	eor	v30.16b,v30.16b,v5.16b
-
-	subs	x3,x3,#128
-	b.lo	.Ltail4x
-
-	b	.Loop4x
-
-.align	4
-.Loop4x:
-	eor	v16.16b,v4.16b,v0.16b
-	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
-	ext	v3.16b,v16.16b,v16.16b,#8
-#ifndef __ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v7.16b,v7.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v28.2d,v3.2d
-	ext	v25.16b,v7.16b,v7.16b,#8
-	pmull2	v1.1q,v27.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	ext	v24.16b,v6.16b,v6.16b,#8
-	eor	v1.16b,v1.16b,v30.16b
-	ext	v23.16b,v5.16b,v5.16b,#8
-
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
-	eor	v7.16b,v7.16b,v25.16b
-	eor	v1.16b,v1.16b,v17.16b
-	pmull2	v31.1q,v20.2d,v25.2d
-	eor	v1.16b,v1.16b,v18.16b
-	pmull	v30.1q,v21.1d,v7.1d
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-	pmull2	v24.1q,v22.2d,v24.2d
-	eor	v0.16b,v1.16b,v18.16b
-	pmull2	v6.1q,v21.2d,v6.2d
-
-	eor	v29.16b,v29.16b,v16.16b
-	eor	v31.16b,v31.16b,v24.16b
-	eor	v30.16b,v30.16b,v6.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	eor	v18.16b,v18.16b,v2.16b
-	pmull2	v23.1q,v26.2d,v23.2d
-	pmull	v5.1q,v27.1d,v5.1d
-
-	eor	v0.16b,v0.16b,v18.16b
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-	eor	v30.16b,v30.16b,v5.16b
-
-	subs	x3,x3,#64
-	b.hs	.Loop4x
-
-.Ltail4x:
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v28.2d,v3.2d
-	pmull2	v1.1q,v27.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-
-	adds	x3,x3,#64
-	b.eq	.Ldone4x
-
-	cmp	x3,#32
-	b.lo	.Lone
-	b.eq	.Ltwo
-.Lthree:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d,v5.2d,v6.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	ext	v24.16b,v6.16b,v6.16b,#8
-	ext	v23.16b,v5.16b,v5.16b,#8
-	eor	v0.16b,v1.16b,v18.16b
-
-	pmull	v29.1q,v20.1d,v24.1d		//H·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	pmull2	v31.1q,v20.2d,v24.2d
-	pmull	v30.1q,v21.1d,v6.1d
-	eor	v0.16b,v0.16b,v18.16b
-	pmull	v7.1q,v22.1d,v23.1d		//H^2·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	pmull2	v23.1q,v22.2d,v23.2d
-	eor	v16.16b,v4.16b,v0.16b
-	pmull2	v5.1q,v21.2d,v5.2d
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	eor	v30.16b,v30.16b,v5.16b
-
-	pmull	v0.1q,v26.1d,v3.1d		//H^3·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v26.2d,v3.2d
-	pmull	v1.1q,v27.1d,v16.1d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-	b	.Ldone4x
-
-.align	4
-.Ltwo:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d,v5.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	ext	v23.16b,v5.16b,v5.16b,#8
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	pmull	v29.1q,v20.1d,v23.1d		//H·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull2	v31.1q,v20.2d,v23.2d
-	pmull	v30.1q,v21.1d,v5.1d
-
-	pmull	v0.1q,v22.1d,v3.1d		//H^2·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v22.2d,v3.2d
-	pmull2	v1.1q,v21.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-	b	.Ldone4x
-
-.align	4
-.Lone:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull	v0.1q,v20.1d,v3.1d
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v20.2d,v3.2d
-	pmull	v1.1q,v21.1d,v16.1d
-
-.Ldone4x:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	eor	v1.16b,v1.16b,v18.16b
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-#ifndef __ARMEB__
-	rev64	v0.16b,v0.16b
-#endif
-	st1	{v0.2d},[x0]		//write out Xi
-
-	ret
-.size	gcm_ghash_v8_4x,.-gcm_ghash_v8_4x
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
 #endif
-#endif
 #endif  // !OPENSSL_NO_ASM
 .section	.note.GNU-stack,"",%progbits
diff --git a/linux-arm/crypto/fipsmodule/ghashv8-armx32.S b/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
index 096dfb7..b97457b 100644
--- a/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
+++ b/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
@@ -15,7 +15,6 @@
 #endif
 #include <openssl/arm_arch.h>
 
-#if __ARM_MAX_ARCH__>=7
 .text
 .fpu	neon
 .code	32
@@ -68,7 +67,8 @@
 	vext.8	q9,q14,q14,#8		@ Karatsuba pre-processing
 	veor	q9,q9,q14
 	vext.8	q13,q8,q9,#8		@ pack Karatsuba pre-processed
-	vst1.64	{q13,q14},[r0]!	@ store Htable[1..2]
+	vst1.64	{q13,q14},[r0]		@ store Htable[1..2]
+
 	bx	lr
 .size	gcm_init_v8,.-gcm_init_v8
 .globl	gcm_gmult_v8
@@ -252,6 +252,5 @@
 .align	2
 .align	2
 #endif
-#endif
 #endif  // !OPENSSL_NO_ASM
 .section	.note.GNU-stack,"",%progbits
diff --git a/sources.bp b/sources.bp
index f112987..44d7a12 100644
--- a/sources.bp
+++ b/sources.bp
@@ -30,7 +30,6 @@
         "src/crypto/asn1/a_object.c",
         "src/crypto/asn1/a_octet.c",
         "src/crypto/asn1/a_print.c",
-        "src/crypto/asn1/a_strex.c",
         "src/crypto/asn1/a_strnid.c",
         "src/crypto/asn1/a_time.c",
         "src/crypto/asn1/a_type.c",
@@ -170,13 +169,13 @@
         "src/crypto/trust_token/voprf.c",
         "src/crypto/x509/a_digest.c",
         "src/crypto/x509/a_sign.c",
+        "src/crypto/x509/a_strex.c",
         "src/crypto/x509/a_verify.c",
         "src/crypto/x509/algorithm.c",
         "src/crypto/x509/asn1_gen.c",
         "src/crypto/x509/by_dir.c",
         "src/crypto/x509/by_file.c",
         "src/crypto/x509/i2d_pr.c",
-        "src/crypto/x509/name_print.c",
         "src/crypto/x509/rsa_pss.c",
         "src/crypto/x509/t_crl.c",
         "src/crypto/x509/t_req.c",
diff --git a/sources.mk b/sources.mk
index afacea9..9244cd6 100644
--- a/sources.mk
+++ b/sources.mk
@@ -28,7 +28,6 @@
   src/crypto/asn1/a_object.c\
   src/crypto/asn1/a_octet.c\
   src/crypto/asn1/a_print.c\
-  src/crypto/asn1/a_strex.c\
   src/crypto/asn1/a_strnid.c\
   src/crypto/asn1/a_time.c\
   src/crypto/asn1/a_type.c\
@@ -169,13 +168,13 @@
   src/crypto/trust_token/voprf.c\
   src/crypto/x509/a_digest.c\
   src/crypto/x509/a_sign.c\
+  src/crypto/x509/a_strex.c\
   src/crypto/x509/a_verify.c\
   src/crypto/x509/algorithm.c\
   src/crypto/x509/asn1_gen.c\
   src/crypto/x509/by_dir.c\
   src/crypto/x509/by_file.c\
   src/crypto/x509/i2d_pr.c\
-  src/crypto/x509/name_print.c\
   src/crypto/x509/rsa_pss.c\
   src/crypto/x509/t_crl.c\
   src/crypto/x509/t_req.c\
diff --git a/src/.clang-format b/src/.clang-format
index 8f9cb6c..6de1483 100644
--- a/src/.clang-format
+++ b/src/.clang-format
@@ -10,7 +10,6 @@
 IncludeBlocks: Preserve
 TypenameMacros: ['LHASH_OF', 'STACK_OF']
 StatementMacros:
-  - "ASN1_SEQUENCE_END"
   - "DECLARE_ASN1_ALLOC_FUNCTIONS"
   - "DECLARE_ASN1_ALLOC_FUNCTIONS_name"
   - "DECLARE_ASN1_ENCODE_FUNCTIONS"
diff --git a/src/BUILDING.md b/src/BUILDING.md
index 08f004c..f76c571 100644
--- a/src/BUILDING.md
+++ b/src/BUILDING.md
@@ -31,10 +31,8 @@
     `CMAKE_ASM_NASM_COMPILER`.
 
   * C and C++ compilers with C++11 support are required. On Windows, MSVC 14
-    (Visual Studio 2015) or later with Platform SDK 8.1 or later are supported,
-    but newer versions are recommended. We will drop support for Visual Studio
-    2015 in March 2022, five years after the release of Visual Studio 2017.
-    Recent versions of GCC (6.1+) and Clang should work on non-Windows
+    (Visual Studio 2015) or later with Platform SDK 8.1 or later are supported.
+    Recent versions of GCC (4.8+) and Clang should work on non-Windows
     platforms, and maybe on Windows too.
 
   * The most recent stable version of [Go](https://golang.org/dl/) is required.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6a5a6aa..893bca7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -117,7 +117,7 @@
 if(CMAKE_COMPILER_IS_GNUCXX OR CLANG)
   # Note clang-cl is odd and sets both CLANG and MSVC. We base our configuration
   # primarily on our normal Clang one.
-  set(C_CXX_FLAGS "-Werror -Wformat=2 -Wsign-compare -Wmissing-field-initializers -Wwrite-strings -Wvla -Wshadow")
+  set(C_CXX_FLAGS "-Werror -Wformat=2 -Wsign-compare -Wmissing-field-initializers -Wwrite-strings -Wvla")
   if(MSVC)
     # clang-cl sets different default warnings than clang. It also treats -Wall
     # as -Weverything, to match MSVC. Instead -W3 is the alias for -Wall.
@@ -172,6 +172,11 @@
   if(CLANG)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmissing-prototypes")
   endif()
+
+  if(CMAKE_COMPILER_IS_GNUCXX AND "4.8" VERSION_GREATER CMAKE_C_COMPILER_VERSION)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-array-bounds")
+  endif()
+
 elseif(MSVC)
   set(MSVC_DISABLED_WARNINGS_LIST
       "C4061" # enumerator 'identifier' in switch of enum 'enumeration' is not
@@ -249,6 +254,12 @@
   add_definitions("-D_STL_EXTRA_DISABLED_WARNINGS=4774 4987")
 endif()
 
+if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.7.99") OR
+   CLANG)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow")
+endif()
+
 if(CMAKE_COMPILER_IS_GNUCXX)
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
 endif()
@@ -394,7 +405,8 @@
 
 function(go_executable dest package)
   set(godeps "${CMAKE_SOURCE_DIR}/util/godeps.go")
-  if(CMAKE_VERSION VERSION_LESS "3.7" OR NOT CMAKE_GENERATOR STREQUAL "Ninja")
+  if(${CMAKE_VERSION} VERSION_LESS "3.7" OR
+     NOT ${CMAKE_GENERATOR} STREQUAL "Ninja")
     # The DEPFILE parameter to add_custom_command is new as of CMake 3.7 and
     # only works with Ninja. Query the sources at configure time. Additionally,
     # everything depends on go.mod. That affects what external packages to use.
@@ -436,7 +448,7 @@
 # builds.
 if(NOT OPENSSL_NO_ASM AND CMAKE_OSX_ARCHITECTURES)
   list(LENGTH CMAKE_OSX_ARCHITECTURES NUM_ARCHES)
-  if(NOT NUM_ARCHES EQUAL 1)
+  if(NOT ${NUM_ARCHES} EQUAL 1)
     message(FATAL_ERROR "Universal binaries not supported.")
   endif()
   list(GET CMAKE_OSX_ARCHITECTURES 0 CMAKE_SYSTEM_PROCESSOR)
@@ -449,44 +461,44 @@
 if(OPENSSL_NO_ASM)
   add_definitions(-DOPENSSL_NO_ASM)
   set(ARCH "generic")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
   set(ARCH "x86_64")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "amd64")
   set(ARCH "x86_64")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64")
   # cmake reports AMD64 on Windows, but we might be building for 32-bit.
   if(CMAKE_SIZEOF_VOID_P EQUAL 8)
     set(ARCH "x86_64")
   else()
     set(ARCH "x86")
   endif()
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86")
   set(ARCH "x86")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386")
   set(ARCH "x86")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i686")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686")
   set(ARCH "x86")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
   set(ARCH "aarch64")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ARM64")
   set(ARCH "aarch64")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64")
   set(ARCH "aarch64")
 # Apple A12 Bionic chipset which is added in iPhone XS/XS Max/XR uses arm64e architecture.
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64e")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64e")
   set(ARCH "aarch64")
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm*")
+elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm*")
   set(ARCH "arm")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "mips")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "mips")
   # Just to avoid the “unknown processor” error.
   set(ARCH "generic")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64le")
   set(ARCH "ppc64le")
 else()
   message(FATAL_ERROR "Unknown processor:" ${CMAKE_SYSTEM_PROCESSOR})
 endif()
 
-if(ANDROID AND NOT ANDROID_NDK_REVISION AND ARCH STREQUAL "arm")
+if(ANDROID AND NOT ANDROID_NDK_REVISION AND ${ARCH} STREQUAL "arm")
   # The third-party Android-NDK CMake files somehow fail to set the -march flag
   # for assembly files. Without this flag, the compiler believes that it's
   # building for ARMv5.
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 990df00..7832edb 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -2,7 +2,7 @@
 
 if(NOT OPENSSL_NO_ASM)
   if(UNIX)
-    if(ARCH STREQUAL "aarch64")
+    if(${ARCH} STREQUAL "aarch64")
       # The "armx" Perl scripts look for "64" in the style argument
       # in order to decide whether to generate 32- or 64-bit asm.
       if(APPLE)
@@ -10,16 +10,16 @@
       else()
         set(PERLASM_STYLE linux64)
       endif()
-    elseif(ARCH STREQUAL "arm")
+    elseif(${ARCH} STREQUAL "arm")
       if(APPLE)
         set(PERLASM_STYLE ios32)
       else()
         set(PERLASM_STYLE linux32)
       endif()
-    elseif(ARCH STREQUAL "ppc64le")
+    elseif(${ARCH} STREQUAL "ppc64le")
       set(PERLASM_STYLE linux64le)
     else()
-      if(ARCH STREQUAL "x86")
+      if(${ARCH} STREQUAL "x86")
         set(PERLASM_FLAGS "-fPIC -DOPENSSL_IA32_SSE2")
       endif()
       if(APPLE)
@@ -47,12 +47,12 @@
       endforeach()
     endif()
   else()
-    if(ARCH STREQUAL "aarch64")
+    if(${ARCH} STREQUAL "aarch64")
       set(PERLASM_STYLE win64)
       set(ASM_EXT S)
       enable_language(ASM)
     else()
-      if(ARCH STREQUAL "x86_64")
+      if(${ARCH} STREQUAL "x86_64")
         set(PERLASM_STYLE nasm)
       else()
         set(PERLASM_STYLE win32n)
@@ -69,7 +69,7 @@
 
 function(perlasm dest src)
   get_filename_component(dir ${dest} DIRECTORY)
-  if(dir STREQUAL "")
+  if ("${dir}" STREQUAL "")
     set(dir ".")
   endif()
 
@@ -104,7 +104,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "arm")
+if(${ARCH} STREQUAL "arm")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -115,7 +115,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "aarch64")
+if(${ARCH} STREQUAL "aarch64")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -124,7 +124,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "ppc64le")
+if(${ARCH} STREQUAL "ppc64le")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -132,7 +132,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "x86")
+if(${ARCH} STREQUAL "x86")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -141,7 +141,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "x86_64")
+if(${ARCH} STREQUAL "x86_64")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -211,7 +211,6 @@
   asn1/a_object.c
   asn1/a_octet.c
   asn1/a_print.c
-  asn1/a_strex.c
   asn1/a_strnid.c
   asn1/a_time.c
   asn1/a_type.c
@@ -351,13 +350,13 @@
   trust_token/voprf.c
   x509/a_digest.c
   x509/a_sign.c
+  x509/a_strex.c
   x509/a_verify.c
   x509/algorithm.c
   x509/asn1_gen.c
   x509/by_dir.c
   x509/by_file.c
   x509/i2d_pr.c
-  x509/name_print.c
   x509/rsa_pss.c
   x509/t_crl.c
   x509/t_req.c
diff --git a/src/crypto/asn1/a_bool.c b/src/crypto/asn1/a_bool.c
index 15396ff..64ae9e5 100644
--- a/src/crypto/asn1/a_bool.c
+++ b/src/crypto/asn1/a_bool.c
@@ -71,7 +71,7 @@
     if (*pp == NULL) {
         if ((p = allocated = OPENSSL_malloc(r)) == NULL) {
             OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-            return -1;
+            return 0;
         }
     } else {
         p = *pp;
diff --git a/src/crypto/asn1/a_d2i_fp.c b/src/crypto/asn1/a_d2i_fp.c
index d0d6d03..fd423e2 100644
--- a/src/crypto/asn1/a_d2i_fp.c
+++ b/src/crypto/asn1/a_d2i_fp.c
@@ -78,6 +78,7 @@
     return ret;
 }
 
+#ifndef OPENSSL_NO_FP_API
 void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)
 {
     BIO *b = BIO_new_fp(in, BIO_NOCLOSE);
@@ -89,3 +90,4 @@
     BIO_free(b);
     return ret;
 }
+#endif
diff --git a/src/crypto/asn1/a_gentm.c b/src/crypto/asn1/a_gentm.c
index 3e6f14e..c91d506 100644
--- a/src/crypto/asn1/a_gentm.c
+++ b/src/crypto/asn1/a_gentm.c
@@ -237,11 +237,6 @@
             goto err;
     }
 
-    if (ts->tm_year < 0 - 1900 || ts->tm_year > 9999 - 1900) {
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_TIME_VALUE);
-        goto err;
-    }
-
     p = (char *)tmps->data;
     if ((p == NULL) || ((size_t)tmps->length < len)) {
         p = OPENSSL_malloc(len);
diff --git a/src/crypto/asn1/a_mbstr.c b/src/crypto/asn1/a_mbstr.c
index 5853b72..953095c 100644
--- a/src/crypto/asn1/a_mbstr.c
+++ b/src/crypto/asn1/a_mbstr.c
@@ -66,6 +66,8 @@
 #include "internal.h"
 #include "../bytestring/internal.h"
 
+static int is_printable(uint32_t value);
+
 /*
  * These functions take a string in UTF8, ASCII or multibyte form and a mask
  * of permissible ASN1 string types. It then works out the minimal type
@@ -151,7 +153,7 @@
         }
 
         /* Update which output formats are still possible. */
-        if ((mask & B_ASN1_PRINTABLESTRING) && !asn1_is_printable(c)) {
+        if ((mask & B_ASN1_PRINTABLESTRING) && !is_printable(c)) {
             mask &= ~B_ASN1_PRINTABLESTRING;
         }
         if ((mask & B_ASN1_IA5STRING) && (c > 127)) {
@@ -206,14 +208,11 @@
         encode_func = cbb_add_utf32_be;
         size_estimate = 4 * nchar;
         outform = MBSTRING_UNIV;
-    } else if (mask & B_ASN1_UTF8STRING) {
+    } else {
         str_type = V_ASN1_UTF8STRING;
         outform = MBSTRING_UTF8;
         encode_func = cbb_add_utf8;
         size_estimate = utf8_len;
-    } else {
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_CHARACTERS);
-        return -1;
     }
 
     if (!out)
@@ -283,16 +282,24 @@
     return -1;
 }
 
-int asn1_is_printable(uint32_t value)
+/* Return 1 if the character is permitted in a PrintableString */
+static int is_printable(uint32_t value)
 {
-    if (value > 0x7f) {
+    int ch;
+    if (value > 0x7f)
         return 0;
-    }
-    /* Note we cannot use |isalnum| because it is locale-dependent. */
-    return ('a' <= value && value <= 'z') ||  //
-           ('A' <= value && value <= 'Z') ||  //
-           ('0' <= value && value <= '9') ||  //
-           value == ' ' || value == '\'' || value == '(' || value == ')' ||
-           value == '+' || value == ',' || value == '-' || value == '.' ||
-           value == '/' || value == ':' || value == '=' || value == '?';
+    ch = (int)value;
+    /*
+     * Note: we can't use 'isalnum' because certain accented characters may
+     * count as alphanumeric in some environments.
+     */
+    if ((ch >= 'a') && (ch <= 'z'))
+        return 1;
+    if ((ch >= 'A') && (ch <= 'Z'))
+        return 1;
+    if ((ch >= '0') && (ch <= '9'))
+        return 1;
+    if ((ch == ' ') || strchr("'()+,-./:=?", ch))
+        return 1;
+    return 0;
 }
diff --git a/src/crypto/asn1/a_object.c b/src/crypto/asn1/a_object.c
index b88f06b..de27e87 100644
--- a/src/crypto/asn1/a_object.c
+++ b/src/crypto/asn1/a_object.c
@@ -69,26 +69,20 @@
 
 int i2d_ASN1_OBJECT(const ASN1_OBJECT *a, unsigned char **pp)
 {
-    if (a == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_PASSED_NULL_PARAMETER);
-        return -1;
-    }
-
-    if (a->length == 0) {
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_OBJECT);
-        return -1;
-    }
-
-    int objsize = ASN1_object_size(0, a->length, V_ASN1_OBJECT);
-    if (pp == NULL || objsize == -1) {
-        return objsize;
-    }
-
     unsigned char *p, *allocated = NULL;
+    int objsize;
+
+    if ((a == NULL) || (a->data == NULL))
+        return (0);
+
+    objsize = ASN1_object_size(0, a->length, V_ASN1_OBJECT);
+    if (pp == NULL || objsize == -1)
+        return objsize;
+
     if (*pp == NULL) {
         if ((p = allocated = OPENSSL_malloc(objsize)) == NULL) {
             OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-            return -1;
+            return 0;
         }
     } else {
         p = *pp;
diff --git a/src/crypto/asn1/a_print.c b/src/crypto/asn1/a_print.c
index c7efede..2104521 100644
--- a/src/crypto/asn1/a_print.c
+++ b/src/crypto/asn1/a_print.c
@@ -56,28 +56,38 @@
 
 #include <openssl/asn1.h>
 
-#include <string.h>
-
-#include "internal.h"
-
+#include <openssl/err.h>
+#include <openssl/mem.h>
 
 int ASN1_PRINTABLE_type(const unsigned char *s, int len)
 {
-    if (len < 0) {
-        len = strlen((const char *)s);
-    }
+    int c;
+    int ia5 = 0;
+    int t61 = 0;
 
-    int printable = 1;
-    for (int i = 0; i < len; i++) {
-        unsigned char c = s[i];
-        if (c & 0x80) {
-            /* No need to continue iterating. */
-            return V_ASN1_T61STRING;
-        }
-        if (!asn1_is_printable(c)) {
-            printable = 0;
-        }
-    }
+    if (len <= 0)
+        len = -1;
+    if (s == NULL)
+        return (V_ASN1_PRINTABLESTRING);
 
-    return printable ? V_ASN1_PRINTABLESTRING : V_ASN1_IA5STRING;
+    while ((*s) && (len-- != 0)) {
+        c = *(s++);
+        if (!(((c >= 'a') && (c <= 'z')) ||
+              ((c >= 'A') && (c <= 'Z')) ||
+              (c == ' ') ||
+              ((c >= '0') && (c <= '9')) ||
+              (c == ' ') || (c == '\'') ||
+              (c == '(') || (c == ')') ||
+              (c == '+') || (c == ',') ||
+              (c == '-') || (c == '.') ||
+              (c == '/') || (c == ':') || (c == '=') || (c == '?')))
+            ia5 = 1;
+        if (c & 0x80)
+            t61 = 1;
+    }
+    if (t61)
+        return (V_ASN1_T61STRING);
+    if (ia5)
+        return (V_ASN1_IA5STRING);
+    return (V_ASN1_PRINTABLESTRING);
 }
diff --git a/src/crypto/asn1/a_strex.c b/src/crypto/asn1/a_strex.c
deleted file mode 100644
index 7829d67..0000000
--- a/src/crypto/asn1/a_strex.c
+++ /dev/null
@@ -1,650 +0,0 @@
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.] */
-
-#include <openssl/asn1.h>
-
-#include <ctype.h>
-#include <inttypes.h>
-#include <string.h>
-
-#include <openssl/bio.h>
-#include <openssl/mem.h>
-
-#include "charmap.h"
-#include "internal.h"
-
-
-// These flags must be distinct from |ESC_FLAGS| and fit in a byte.
-
-// Character is a valid PrintableString character
-#define CHARTYPE_PRINTABLESTRING 0x10
-// Character needs escaping if it is the first character
-#define CHARTYPE_FIRST_ESC_2253 0x20
-// Character needs escaping if it is the last character
-#define CHARTYPE_LAST_ESC_2253 0x40
-
-#define CHARTYPE_BS_ESC         (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
-
-#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
-                  ASN1_STRFLGS_ESC_QUOTE | \
-                  ASN1_STRFLGS_ESC_CTRL | \
-                  ASN1_STRFLGS_ESC_MSB)
-
-static int maybe_write(BIO *out, const void *buf, int len)
-{
-    /* If |out| is NULL, ignore the output but report the length. */
-    return out == NULL || BIO_write(out, buf, len) == len;
-}
-
-/*
- * This function handles display of strings, one character at a time. It is
- * passed an unsigned long for each character because it could come from 2 or
- * even 4 byte forms.
- */
-
-#define HEX_SIZE(type) (sizeof(type)*2)
-
-static int do_esc_char(uint32_t c, unsigned char flags, char *do_quotes,
-                       BIO *out)
-{
-    unsigned char chflgs, chtmp;
-    char tmphex[HEX_SIZE(uint32_t) + 3];
-
-    if (c > 0xffff) {
-        BIO_snprintf(tmphex, sizeof tmphex, "\\W%08" PRIX32, c);
-        if (!maybe_write(out, tmphex, 10))
-            return -1;
-        return 10;
-    }
-    if (c > 0xff) {
-        BIO_snprintf(tmphex, sizeof tmphex, "\\U%04" PRIX32, c);
-        if (!maybe_write(out, tmphex, 6))
-            return -1;
-        return 6;
-    }
-    chtmp = (unsigned char)c;
-    if (chtmp > 0x7f)
-        chflgs = flags & ASN1_STRFLGS_ESC_MSB;
-    else
-        chflgs = char_type[chtmp] & flags;
-    if (chflgs & CHARTYPE_BS_ESC) {
-        /* If we don't escape with quotes, signal we need quotes */
-        if (chflgs & ASN1_STRFLGS_ESC_QUOTE) {
-            if (do_quotes)
-                *do_quotes = 1;
-            if (!maybe_write(out, &chtmp, 1))
-                return -1;
-            return 1;
-        }
-        if (!maybe_write(out, "\\", 1))
-            return -1;
-        if (!maybe_write(out, &chtmp, 1))
-            return -1;
-        return 2;
-    }
-    if (chflgs & (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB)) {
-        BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
-        if (!maybe_write(out, tmphex, 3))
-            return -1;
-        return 3;
-    }
-    /*
-     * If we get this far and do any escaping at all must escape the escape
-     * character itself: backslash.
-     */
-    if (chtmp == '\\' && flags & ESC_FLAGS) {
-        if (!maybe_write(out, "\\\\", 2))
-            return -1;
-        return 2;
-    }
-    if (!maybe_write(out, &chtmp, 1))
-        return -1;
-    return 1;
-}
-
-#define BUF_TYPE_WIDTH_MASK     0x7
-#define BUF_TYPE_CONVUTF8       0x8
-
-/*
- * This function sends each character in a buffer to do_esc_char(). It
- * interprets the content formats and converts to or from UTF8 as
- * appropriate.
- */
-
-static int do_buf(unsigned char *buf, int buflen,
-                  int type, unsigned char flags, char *quotes, BIO *out)
-{
-    int i, outlen, len, charwidth;
-    unsigned char orflags, *p, *q;
-    uint32_t c;
-    p = buf;
-    q = buf + buflen;
-    outlen = 0;
-    charwidth = type & BUF_TYPE_WIDTH_MASK;
-
-    switch (charwidth) {
-    case 4:
-        if (buflen & 3) {
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_UNIVERSALSTRING);
-            return -1;
-        }
-        break;
-    case 2:
-        if (buflen & 1) {
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_BMPSTRING);
-            return -1;
-        }
-        break;
-    default:
-        break;
-    }
-
-    while (p != q) {
-        if (p == buf && flags & ASN1_STRFLGS_ESC_2253)
-            orflags = CHARTYPE_FIRST_ESC_2253;
-        else
-            orflags = 0;
-        /* TODO(davidben): Replace this with |cbs_get_ucs2_be|, etc., to check
-         * for invalid codepoints. */
-        switch (charwidth) {
-        case 4:
-            c = ((uint32_t)*p++) << 24;
-            c |= ((uint32_t)*p++) << 16;
-            c |= ((uint32_t)*p++) << 8;
-            c |= *p++;
-            break;
-
-        case 2:
-            c = ((uint32_t)*p++) << 8;
-            c |= *p++;
-            break;
-
-        case 1:
-            c = *p++;
-            break;
-
-        case 0:
-            i = UTF8_getc(p, buflen, &c);
-            if (i < 0)
-                return -1;      /* Invalid UTF8String */
-            buflen -= i;
-            p += i;
-            break;
-        default:
-            return -1;          /* invalid width */
-        }
-        if (p == q && flags & ASN1_STRFLGS_ESC_2253)
-            orflags = CHARTYPE_LAST_ESC_2253;
-        if (type & BUF_TYPE_CONVUTF8) {
-            unsigned char utfbuf[6];
-            int utflen;
-            utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
-            for (i = 0; i < utflen; i++) {
-                /*
-                 * We don't need to worry about setting orflags correctly
-                 * because if utflen==1 its value will be correct anyway
-                 * otherwise each character will be > 0x7f and so the
-                 * character will never be escaped on first and last.
-                 */
-                len = do_esc_char(utfbuf[i], (unsigned char)(flags | orflags),
-                                  quotes, out);
-                if (len < 0)
-                    return -1;
-                outlen += len;
-            }
-        } else {
-            len = do_esc_char(c, (unsigned char)(flags | orflags), quotes, out);
-            if (len < 0)
-                return -1;
-            outlen += len;
-        }
-    }
-    return outlen;
-}
-
-/* This function hex dumps a buffer of characters */
-
-static int do_hex_dump(BIO *out, unsigned char *buf, int buflen)
-{
-    static const char hexdig[] = "0123456789ABCDEF";
-    unsigned char *p, *q;
-    char hextmp[2];
-    if (out) {
-        p = buf;
-        q = buf + buflen;
-        while (p != q) {
-            hextmp[0] = hexdig[*p >> 4];
-            hextmp[1] = hexdig[*p & 0xf];
-            if (!maybe_write(out, hextmp, 2))
-                return -1;
-            p++;
-        }
-    }
-    return buflen << 1;
-}
-
-/*
- * "dump" a string. This is done when the type is unknown, or the flags
- * request it. We can either dump the content octets or the entire DER
- * encoding. This uses the RFC 2253 #01234 format.
- */
-
-static int do_dump(unsigned long lflags, BIO *out, const ASN1_STRING *str)
-{
-    if (!maybe_write(out, "#", 1)) {
-        return -1;
-    }
-
-    /* If we don't dump DER encoding just dump content octets */
-    if (!(lflags & ASN1_STRFLGS_DUMP_DER)) {
-        int outlen = do_hex_dump(out, str->data, str->length);
-        if (outlen < 0) {
-            return -1;
-        }
-        return outlen + 1;
-    }
-
-    /*
-     * Placing the ASN1_STRING in a temporary ASN1_TYPE allows the DER encoding
-     * to readily obtained.
-     */
-    ASN1_TYPE t;
-    t.type = str->type;
-    /* Negative INTEGER and ENUMERATED values are the only case where
-     * |ASN1_STRING| and |ASN1_TYPE| types do not match.
-     *
-     * TODO(davidben): There are also some type fields which, in |ASN1_TYPE|, do
-     * not correspond to |ASN1_STRING|. It is unclear whether those are allowed
-     * in |ASN1_STRING| at all, or what the space of allowed types is.
-     * |ASN1_item_ex_d2i| will never produce such a value so, for now, we say
-     * this is an invalid input. But this corner of the library in general
-     * should be more robust. */
-    if (t.type == V_ASN1_NEG_INTEGER) {
-      t.type = V_ASN1_INTEGER;
-    } else if (t.type == V_ASN1_NEG_ENUMERATED) {
-      t.type = V_ASN1_ENUMERATED;
-    }
-    t.value.asn1_string = (ASN1_STRING *)str;
-    unsigned char *der_buf = NULL;
-    int der_len = i2d_ASN1_TYPE(&t, &der_buf);
-    if (der_len < 0) {
-        return -1;
-    }
-    int outlen = do_hex_dump(out, der_buf, der_len);
-    OPENSSL_free(der_buf);
-    if (outlen < 0) {
-        return -1;
-    }
-    return outlen + 1;
-}
-
-/*
- * Lookup table to convert tags to character widths, 0 = UTF8 encoded, -1 is
- * used for non string types otherwise it is the number of bytes per
- * character
- */
-
-static const signed char tag2nbyte[] = {
-    -1, -1, -1, -1, -1,         /* 0-4 */
-    -1, -1, -1, -1, -1,         /* 5-9 */
-    -1, -1, 0, -1,              /* 10-13 */
-    -1, -1, -1, -1,             /* 15-17 */
-    1, 1, 1,                    /* 18-20 */
-    -1, 1, 1, 1,                /* 21-24 */
-    -1, 1, -1,                  /* 25-27 */
-    4, -1, 2                    /* 28-30 */
-};
-
-/*
- * This is the main function, print out an ASN1_STRING taking note of various
- * escape and display options. Returns number of characters written or -1 if
- * an error occurred.
- */
-
-int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long lflags)
-{
-    int outlen, len;
-    int type;
-    char quotes;
-    unsigned char flags;
-    quotes = 0;
-    /* Keep a copy of escape flags */
-    flags = (unsigned char)(lflags & ESC_FLAGS);
-
-    type = str->type;
-
-    outlen = 0;
-
-    if (lflags & ASN1_STRFLGS_SHOW_TYPE) {
-        const char *tagname;
-        tagname = ASN1_tag2str(type);
-        outlen += strlen(tagname);
-        if (!maybe_write(out, tagname, outlen) || !maybe_write(out, ":", 1))
-            return -1;
-        outlen++;
-    }
-
-    /* Decide what to do with type, either dump content or display it */
-
-    /* Dump everything */
-    if (lflags & ASN1_STRFLGS_DUMP_ALL)
-        type = -1;
-    /* Ignore the string type */
-    else if (lflags & ASN1_STRFLGS_IGNORE_TYPE)
-        type = 1;
-    else {
-        /* Else determine width based on type */
-        if ((type > 0) && (type < 31))
-            type = tag2nbyte[type];
-        else
-            type = -1;
-        if ((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN))
-            type = 1;
-    }
-
-    if (type == -1) {
-        len = do_dump(lflags, out, str);
-        if (len < 0)
-            return -1;
-        outlen += len;
-        return outlen;
-    }
-
-    if (lflags & ASN1_STRFLGS_UTF8_CONVERT) {
-        /*
-         * Note: if string is UTF8 and we want to convert to UTF8 then we
-         * just interpret it as 1 byte per character to avoid converting
-         * twice.
-         */
-        if (!type)
-            type = 1;
-        else
-            type |= BUF_TYPE_CONVUTF8;
-    }
-
-    len = do_buf(str->data, str->length, type, flags, &quotes, NULL);
-    if (len < 0)
-        return -1;
-    outlen += len;
-    if (quotes)
-        outlen += 2;
-    if (!out)
-        return outlen;
-    if (quotes && !maybe_write(out, "\"", 1))
-        return -1;
-    if (do_buf(str->data, str->length, type, flags, NULL, out) < 0)
-        return -1;
-    if (quotes && !maybe_write(out, "\"", 1))
-        return -1;
-    return outlen;
-}
-
-int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
-                            unsigned long flags)
-{
-    BIO *bio = NULL;
-    if (fp != NULL) {
-        /* If |fp| is NULL, this function returns the number of bytes without
-         * writing. */
-        bio = BIO_new_fp(fp, BIO_NOCLOSE);
-        if (bio == NULL) {
-            return -1;
-        }
-    }
-    int ret = ASN1_STRING_print_ex(bio, str, flags);
-    BIO_free(bio);
-    return ret;
-}
-
-int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in)
-{
-    ASN1_STRING stmp, *str = &stmp;
-    int mbflag, type, ret;
-    if (!in)
-        return -1;
-    type = in->type;
-    if ((type < 0) || (type > 30))
-        return -1;
-    mbflag = tag2nbyte[type];
-    if (mbflag == -1)
-        return -1;
-    mbflag |= MBSTRING_FLAG;
-    stmp.data = NULL;
-    stmp.length = 0;
-    stmp.flags = 0;
-    ret = ASN1_mbstring_copy(&str, in->data, in->length, mbflag,
-                             B_ASN1_UTF8STRING);
-    if (ret < 0)
-        return ret;
-    *out = stmp.data;
-    return stmp.length;
-}
-
-int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
-{
-    int i, n;
-    char buf[80];
-    const char *p;
-
-    if (v == NULL)
-        return (0);
-    n = 0;
-    p = (const char *)v->data;
-    for (i = 0; i < v->length; i++) {
-        if ((p[i] > '~') || ((p[i] < ' ') &&
-                             (p[i] != '\n') && (p[i] != '\r')))
-            buf[n] = '.';
-        else
-            buf[n] = p[i];
-        n++;
-        if (n >= 80) {
-            if (BIO_write(bp, buf, n) <= 0)
-                return (0);
-            n = 0;
-        }
-    }
-    if (n > 0)
-        if (BIO_write(bp, buf, n) <= 0)
-            return (0);
-    return (1);
-}
-
-int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
-{
-    if (tm->type == V_ASN1_UTCTIME)
-        return ASN1_UTCTIME_print(bp, tm);
-    if (tm->type == V_ASN1_GENERALIZEDTIME)
-        return ASN1_GENERALIZEDTIME_print(bp, tm);
-    BIO_write(bp, "Bad time value", 14);
-    return (0);
-}
-
-static const char *const mon[12] = {
-    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
-{
-    char *v;
-    int gmt = 0;
-    int i;
-    int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
-    char *f = NULL;
-    int f_len = 0;
-
-    i = tm->length;
-    v = (char *)tm->data;
-
-    if (i < 12)
-        goto err;
-    if (v[i - 1] == 'Z')
-        gmt = 1;
-    for (i = 0; i < 12; i++)
-        if ((v[i] > '9') || (v[i] < '0'))
-            goto err;
-    y = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] -
-                                                                        '0');
-    M = (v[4] - '0') * 10 + (v[5] - '0');
-    if ((M > 12) || (M < 1))
-        goto err;
-    d = (v[6] - '0') * 10 + (v[7] - '0');
-    h = (v[8] - '0') * 10 + (v[9] - '0');
-    m = (v[10] - '0') * 10 + (v[11] - '0');
-    if (tm->length >= 14 &&
-        (v[12] >= '0') && (v[12] <= '9') &&
-        (v[13] >= '0') && (v[13] <= '9')) {
-        s = (v[12] - '0') * 10 + (v[13] - '0');
-        /* Check for fractions of seconds. */
-        if (tm->length >= 15 && v[14] == '.') {
-            int l = tm->length;
-            f = &v[14];         /* The decimal point. */
-            f_len = 1;
-            while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
-                ++f_len;
-        }
-    }
-
-    if (BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
-                   mon[M - 1], d, h, m, s, f_len, f, y,
-                   (gmt) ? " GMT" : "") <= 0)
-        return (0);
-    else
-        return (1);
- err:
-    BIO_write(bp, "Bad time value", 14);
-    return (0);
-}
-
-// consume_two_digits is a helper function for ASN1_UTCTIME_print. If |*v|,
-// assumed to be |*len| bytes long, has two leading digits, updates |*out| with
-// their value, updates |v| and |len|, and returns one. Otherwise, returns
-// zero.
-static int consume_two_digits(int* out, const char **v, int *len) {
-  if (*len < 2|| !isdigit((*v)[0]) || !isdigit((*v)[1])) {
-    return 0;
-  }
-  *out = ((*v)[0] - '0') * 10 + ((*v)[1] - '0');
-  *len -= 2;
-  *v += 2;
-  return 1;
-}
-
-// consume_zulu_timezone is a helper function for ASN1_UTCTIME_print. If |*v|,
-// assumed to be |*len| bytes long, starts with "Z" then it updates |*v| and
-// |*len| and returns one. Otherwise returns zero.
-static int consume_zulu_timezone(const char **v, int *len) {
-  if (*len == 0 || (*v)[0] != 'Z') {
-    return 0;
-  }
-
-  *len -= 1;
-  *v += 1;
-  return 1;
-}
-
-int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm) {
-  const char *v = (const char *)tm->data;
-  int len = tm->length;
-  int Y = 0, M = 0, D = 0, h = 0, m = 0, s = 0;
-
-  // YYMMDDhhmm are required to be present.
-  if (!consume_two_digits(&Y, &v, &len) ||
-      !consume_two_digits(&M, &v, &len) ||
-      !consume_two_digits(&D, &v, &len) ||
-      !consume_two_digits(&h, &v, &len) ||
-      !consume_two_digits(&m, &v, &len)) {
-    goto err;
-  }
-  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires seconds
-  // to be present, but historically this code has forgiven its absence.
-  consume_two_digits(&s, &v, &len);
-
-  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, specifies this
-  // interpretation of the year.
-  if (Y < 50) {
-    Y += 2000;
-  } else {
-    Y += 1900;
-  }
-  if (M > 12 || M == 0) {
-    goto err;
-  }
-  if (D > 31 || D == 0) {
-    goto err;
-  }
-  if (h > 23 || m > 59 || s > 60) {
-    goto err;
-  }
-
-  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires the "Z"
-  // to be present, but historically this code has forgiven its absence.
-  const int is_gmt = consume_zulu_timezone(&v, &len);
-
-  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, does not permit
-  // the specification of timezones using the +hhmm / -hhmm syntax, which is
-  // the only other thing that might legitimately be found at the end.
-  if (len) {
-    goto err;
-  }
-
-  return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s", mon[M - 1], D, h, m, s, Y,
-                    is_gmt ? " GMT" : "") > 0;
-
-err:
-  BIO_write(bp, "Bad time value", 14);
-  return 0;
-}
diff --git a/src/crypto/asn1/a_strnid.c b/src/crypto/asn1/a_strnid.c
index f7ad084..efbf0fa 100644
--- a/src/crypto/asn1/a_strnid.c
+++ b/src/crypto/asn1/a_strnid.c
@@ -69,17 +69,53 @@
 static STACK_OF(ASN1_STRING_TABLE) *stable = NULL;
 static void st_free(ASN1_STRING_TABLE *tbl);
 
+/*
+ * This is the global mask for the mbstring functions: this is use to mask
+ * out certain types (such as BMPString and UTF8String) because certain
+ * software (e.g. Netscape) has problems with them.
+ */
+
+static unsigned long global_mask = B_ASN1_UTF8STRING;
+
 void ASN1_STRING_set_default_mask(unsigned long mask)
 {
+    global_mask = mask;
 }
 
 unsigned long ASN1_STRING_get_default_mask(void)
 {
-    return B_ASN1_UTF8STRING;
+    return global_mask;
 }
 
+/*
+ * This function sets the default to various "flavours" of configuration.
+ * based on an ASCII string. Currently this is: MASK:XXXX : a numerical mask
+ * value. nobmp : Don't use BMPStrings (just Printable, T61). pkix : PKIX
+ * recommendation in RFC2459. utf8only : only use UTF8Strings (RFC2459
+ * recommendation for 2004). default: the default value, Printable, T61, BMP.
+ */
+
 int ASN1_STRING_set_default_mask_asc(const char *p)
 {
+    unsigned long mask;
+    char *end;
+    if (!strncmp(p, "MASK:", 5)) {
+        if (!p[5])
+            return 0;
+        mask = strtoul(p + 5, &end, 0);
+        if (*end)
+            return 0;
+    } else if (!strcmp(p, "nombstr"))
+        mask = ~((unsigned long)(B_ASN1_BMPSTRING | B_ASN1_UTF8STRING));
+    else if (!strcmp(p, "pkix"))
+        mask = ~((unsigned long)B_ASN1_T61STRING);
+    else if (!strcmp(p, "utf8only"))
+        mask = B_ASN1_UTF8STRING;
+    else if (!strcmp(p, "default"))
+        mask = 0xFFFFFFFFL;
+    else
+        return 0;
+    ASN1_STRING_set_default_mask(mask);
     return 1;
 }
 
@@ -103,12 +139,13 @@
     if (tbl) {
         mask = tbl->mask;
         if (!(tbl->flags & STABLE_NO_MASK))
-            mask &= B_ASN1_UTF8STRING;
+            mask &= global_mask;
         ret = ASN1_mbstring_ncopy(out, in, inlen, inform, mask,
                                   tbl->minsize, tbl->maxsize);
-    } else {
-        ret = ASN1_mbstring_copy(out, in, inlen, inform, B_ASN1_UTF8STRING);
-    }
+    } else
+        ret =
+            ASN1_mbstring_copy(out, in, inlen, inform,
+                               DIRSTRING_TYPE & global_mask);
     if (ret <= 0)
         return NULL;
     return *out;
@@ -118,7 +155,7 @@
  * Now the tables and helper functions for the string table:
  */
 
-/* size limits: this stuff is taken straight from RFC 3280 */
+/* size limits: this stuff is taken straight from RFC3280 */
 
 #define ub_name                         32768
 #define ub_common_name                  64
diff --git a/src/crypto/asn1/a_time.c b/src/crypto/asn1/a_time.c
index ad7f784..15c9409 100644
--- a/src/crypto/asn1/a_time.c
+++ b/src/crypto/asn1/a_time.c
@@ -200,7 +200,7 @@
     return 0;
 }
 
-int ASN1_TIME_diff(int *out_days, int *out_seconds,
+int ASN1_TIME_diff(int *pday, int *psec,
                    const ASN1_TIME *from, const ASN1_TIME *to)
 {
     struct tm tm_from, tm_to;
@@ -208,5 +208,5 @@
         return 0;
     if (!asn1_time_to_tm(&tm_to, to))
         return 0;
-    return OPENSSL_gmtime_diff(out_days, out_seconds, &tm_from, &tm_to);
+    return OPENSSL_gmtime_diff(pday, psec, &tm_from, &tm_to);
 }
diff --git a/src/crypto/asn1/a_utctm.c b/src/crypto/asn1/a_utctm.c
index 21ea2cc..28f07ac 100644
--- a/src/crypto/asn1/a_utctm.c
+++ b/src/crypto/asn1/a_utctm.c
@@ -262,3 +262,42 @@
         return -1;
     return 0;
 }
+
+#if 0
+time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s)
+{
+    struct tm tm;
+    int offset;
+
+    OPENSSL_memset(&tm, '\0', sizeof tm);
+
+# define g2(p) (((p)[0]-'0')*10+(p)[1]-'0')
+    tm.tm_year = g2(s->data);
+    if (tm.tm_year < 50)
+        tm.tm_year += 100;
+    tm.tm_mon = g2(s->data + 2) - 1;
+    tm.tm_mday = g2(s->data + 4);
+    tm.tm_hour = g2(s->data + 6);
+    tm.tm_min = g2(s->data + 8);
+    tm.tm_sec = g2(s->data + 10);
+    if (s->data[12] == 'Z')
+        offset = 0;
+    else {
+        offset = g2(s->data + 13) * 60 + g2(s->data + 15);
+        if (s->data[12] == '-')
+            offset = -offset;
+    }
+# undef g2
+
+    return mktime(&tm) - offset * 60; /* FIXME: mktime assumes the current
+                                       * timezone instead of UTC, and unless
+                                       * we rewrite OpenSSL in Lisp we cannot
+                                       * locally change the timezone without
+                                       * possibly interfering with other
+                                       * parts of the program. timegm, which
+                                       * uses UTC, is non-standard. Also
+                                       * time_t is inappropriate for general
+                                       * UTC times because it may a 32 bit
+                                       * type. */
+}
+#endif
diff --git a/src/crypto/asn1/asn1_par.c b/src/crypto/asn1/asn1_par.c
index 282ad23..b1a01ed 100644
--- a/src/crypto/asn1/asn1_par.c
+++ b/src/crypto/asn1/asn1_par.c
@@ -72,7 +72,7 @@
     };
 
     if ((tag == V_ASN1_NEG_INTEGER) || (tag == V_ASN1_NEG_ENUMERATED))
-        tag &= ~V_ASN1_NEG;
+        tag &= ~0x100;
 
     if (tag < 0 || tag > 30)
         return "(unknown)";
diff --git a/src/crypto/asn1/asn1_test.cc b/src/crypto/asn1/asn1_test.cc
index 9119dea..725542c 100644
--- a/src/crypto/asn1/asn1_test.cc
+++ b/src/crypto/asn1/asn1_test.cc
@@ -15,7 +15,6 @@
 #include <limits.h>
 #include <stdio.h>
 
-#include <string>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -170,8 +169,6 @@
       {V_ASN1_BOOLEAN, {0x01, 0x01, 0x00}},
       // OCTET_STRING { "a" }
       {V_ASN1_OCTET_STRING, {0x04, 0x01, 0x61}},
-      // OCTET_STRING { }
-      {V_ASN1_OCTET_STRING, {0x04, 0x00}},
       // BIT_STRING { `01` `00` }
       {V_ASN1_BIT_STRING, {0x03, 0x02, 0x01, 0x00}},
       // INTEGER { -1 }
@@ -459,666 +456,6 @@
   }
 }
 
-static std::string ASN1StringToStdString(const ASN1_STRING *str) {
-  return std::string(ASN1_STRING_get0_data(str),
-                     ASN1_STRING_get0_data(str) + ASN1_STRING_length(str));
-}
-
-TEST(ASN1Test, SetTime) {
-  static const struct {
-    time_t time;
-    const char *generalized;
-    const char *utc;
-  } kTests[] = {
-    {-631152001, "19491231235959Z", nullptr},
-    {-631152000, "19500101000000Z", "500101000000Z"},
-    {0, "19700101000000Z", "700101000000Z"},
-    {981173106, "20010203040506Z", "010203040506Z"},
-#if defined(OPENSSL_64_BIT)
-    // TODO(https://crbug.com/boringssl/416): These cases overflow 32-bit
-    // |time_t| and do not consistently work on 32-bit platforms. For now,
-    // disable the tests on 32-bit. Re-enable them once the bug is fixed.
-    {2524607999, "20491231235959Z", "491231235959Z"},
-    {2524608000, "20500101000000Z", nullptr},
-    // Test boundary conditions.
-    {-62167219200, "00000101000000Z", nullptr},
-    {-62167219201, nullptr, nullptr},
-    {253402300799, "99991231235959Z", nullptr},
-    {253402300800, nullptr, nullptr},
-#endif
-  };
-  for (const auto &t : kTests) {
-    SCOPED_TRACE(t.time);
-#if defined(OPENSSL_WINDOWS)
-    // Windows |time_t| functions can only handle 1970 through 3000. See
-    // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/gmtime-s-gmtime32-s-gmtime64-s?view=msvc-160
-    if (t.time < 0 || int64_t{t.time} > 32535215999) {
-      continue;
-    }
-#endif
-
-    bssl::UniquePtr<ASN1_UTCTIME> utc(ASN1_UTCTIME_set(nullptr, t.time));
-    if (t.utc) {
-      ASSERT_TRUE(utc);
-      EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(utc.get()));
-      EXPECT_EQ(t.utc, ASN1StringToStdString(utc.get()));
-    } else {
-      EXPECT_FALSE(utc);
-    }
-
-    bssl::UniquePtr<ASN1_GENERALIZEDTIME> generalized(
-        ASN1_GENERALIZEDTIME_set(nullptr, t.time));
-    if (t.generalized) {
-      ASSERT_TRUE(generalized);
-      EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(generalized.get()));
-      EXPECT_EQ(t.generalized, ASN1StringToStdString(generalized.get()));
-    } else {
-      EXPECT_FALSE(generalized);
-    }
-
-    bssl::UniquePtr<ASN1_TIME> choice(ASN1_TIME_set(nullptr, t.time));
-    if (t.generalized) {
-      ASSERT_TRUE(choice);
-      if (t.utc) {
-        EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(choice.get()));
-        EXPECT_EQ(t.utc, ASN1StringToStdString(choice.get()));
-      } else {
-        EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(choice.get()));
-        EXPECT_EQ(t.generalized, ASN1StringToStdString(choice.get()));
-      }
-    } else {
-      EXPECT_FALSE(choice);
-    }
-  }
-}
-
-static std::vector<uint8_t> StringToVector(const std::string &str) {
-  return std::vector<uint8_t>(str.begin(), str.end());
-}
-
-TEST(ASN1Test, StringPrintEx) {
-  const struct {
-    int type;
-    std::vector<uint8_t> data;
-    int str_flags;
-    unsigned long flags;
-    std::string expected;
-  } kTests[] = {
-      // A string like "hello" is never escaped or quoted.
-      // |ASN1_STRFLGS_ESC_QUOTE| only introduces quotes when needed. Note
-      // OpenSSL interprets T61String as Latin-1.
-      {V_ASN1_T61STRING, StringToVector("hello"), 0, 0, "hello"},
-      {V_ASN1_T61STRING, StringToVector("hello"), 0,
-       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB,
-       "hello"},
-      {V_ASN1_T61STRING, StringToVector("hello"), 0,
-       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
-           ASN1_STRFLGS_ESC_QUOTE,
-       "hello"},
-
-      // By default, 8-bit characters are printed without escaping.
-      {V_ASN1_T61STRING,
-       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
-       0,
-       0,
-       std::string(1, '\0') + "\n\x80\xff,+\"\\<>;"},
-
-      // Flags control different escapes. Note that any escape flag will cause
-      // blackslashes to be escaped.
-      {V_ASN1_T61STRING,
-       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
-       0,
-       ASN1_STRFLGS_ESC_2253,
-       std::string(1, '\0') + "\n\x80\xff\\,\\+\\\"\\\\\\<\\>\\;"},
-      {V_ASN1_T61STRING,
-       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
-       0,
-       ASN1_STRFLGS_ESC_CTRL,
-       "\\00\\0A\x80\xff,+\"\\\\<>;"},
-      {V_ASN1_T61STRING,
-       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
-       0,
-       ASN1_STRFLGS_ESC_MSB,
-       std::string(1, '\0') + "\n\\80\\FF,+\"\\\\<>;"},
-      {V_ASN1_T61STRING,
-       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
-       0,
-       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB,
-       "\\00\\0A\\80\\FF\\,\\+\\\"\\\\\\<\\>\\;"},
-
-      // When quoted, fewer characters need to be escaped in RFC 2253.
-      {V_ASN1_T61STRING,
-       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
-       0,
-       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
-           ASN1_STRFLGS_ESC_QUOTE,
-       "\"\\00\\0A\\80\\FF,+\\\"\\\\<>;\""},
-
-      // If no characters benefit from quotes, no quotes are added.
-      {V_ASN1_T61STRING,
-       {0, '\n', 0x80, 0xff, '"', '\\'},
-       0,
-       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
-           ASN1_STRFLGS_ESC_QUOTE,
-       "\\00\\0A\\80\\FF\\\"\\\\"},
-
-      // RFC 2253 only escapes spaces at the start and end of a string.
-      {V_ASN1_T61STRING, StringToVector("   "), 0, ASN1_STRFLGS_ESC_2253,
-       "\\  \\ "},
-      {V_ASN1_T61STRING, StringToVector("   "), 0,
-       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_QUOTE, "\"   \""},
-
-      // RFC 2253 only escapes # at the start of a string.
-      {V_ASN1_T61STRING, StringToVector("###"), 0, ASN1_STRFLGS_ESC_2253,
-       "\\###"},
-      {V_ASN1_T61STRING, StringToVector("###"), 0,
-       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_QUOTE, "\"###\""},
-
-      // By default, strings are decoded and Unicode code points are
-      // individually escaped.
-      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
-       0, ASN1_STRFLGS_ESC_MSB, "a\\80\\U0100\\W00010000"},
-      {V_ASN1_BMPSTRING,
-       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
-       0,
-       ASN1_STRFLGS_ESC_MSB,
-       "a\\80\\U0100"},
-      {V_ASN1_UNIVERSALSTRING,
-       {0x00, 0x00, 0x00, 'a',   //
-        0x00, 0x00, 0x00, 0x80,  //
-        0x00, 0x00, 0x01, 0x00,  //
-        0x00, 0x01, 0x00, 0x00},
-       0,
-       ASN1_STRFLGS_ESC_MSB,
-       "a\\80\\U0100\\W00010000"},
-
-      // |ASN1_STRFLGS_UTF8_CONVERT| normalizes everything to UTF-8 and then
-      // escapes individual bytes.
-      {V_ASN1_IA5STRING, StringToVector("a\x80"), 0,
-       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT, "a\\C2\\80"},
-      {V_ASN1_T61STRING, StringToVector("a\x80"), 0,
-       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT, "a\\C2\\80"},
-      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
-       0, ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
-       "a\\C2\\80\\C4\\80\\F0\\90\\80\\80"},
-      {V_ASN1_BMPSTRING,
-       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
-       0,
-       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
-       "a\\C2\\80\\C4\\80"},
-      {V_ASN1_UNIVERSALSTRING,
-       {0x00, 0x00, 0x00, 'a',   //
-        0x00, 0x00, 0x00, 0x80,  //
-        0x00, 0x00, 0x01, 0x00,  //
-        0x00, 0x01, 0x00, 0x00},
-       0,
-       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
-       "a\\C2\\80\\C4\\80\\F0\\90\\80\\80"},
-
-      // The same as above, but without escaping the UTF-8 encoding.
-      {V_ASN1_IA5STRING, StringToVector("a\x80"), 0, ASN1_STRFLGS_UTF8_CONVERT,
-       "a\xc2\x80"},
-      {V_ASN1_T61STRING, StringToVector("a\x80"), 0, ASN1_STRFLGS_UTF8_CONVERT,
-       "a\xc2\x80"},
-      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
-       0, ASN1_STRFLGS_UTF8_CONVERT, "a\xc2\x80\xc4\x80\xf0\x90\x80\x80"},
-      {V_ASN1_BMPSTRING,
-       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
-       0,
-       ASN1_STRFLGS_UTF8_CONVERT,
-       "a\xc2\x80\xc4\x80"},
-      {V_ASN1_UNIVERSALSTRING,
-       {0x00, 0x00, 0x00, 'a',   //
-        0x00, 0x00, 0x00, 0x80,  //
-        0x00, 0x00, 0x01, 0x00,  //
-        0x00, 0x01, 0x00, 0x00},
-       0,
-       ASN1_STRFLGS_UTF8_CONVERT,
-       "a\xc2\x80\xc4\x80\xf0\x90\x80\x80"},
-
-      // Types that cannot be decoded are, by default, treated as a byte string.
-      {V_ASN1_OCTET_STRING, {0xff}, 0, 0, "\xff"},
-      {-1, {0xff}, 0, 0, "\xff"},
-      {100, {0xff}, 0, 0, "\xff"},
-
-      // |ASN1_STRFLGS_UTF8_CONVERT| still converts these bytes to UTF-8.
-      //
-      // TODO(davidben): This seems like a bug. Although it's unclear because
-      // the non-RFC-2253 options aren't especially sound. Can we just remove
-      // them?
-      {V_ASN1_OCTET_STRING, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
-      {-1, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
-      {100, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
-
-      // |ASN1_STRFLGS_IGNORE_TYPE| causes the string type to be ignored, so it
-      // is always treated as a byte string, even if it is not a valid encoding.
-      {V_ASN1_UTF8STRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
-      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
-      {V_ASN1_UNIVERSALSTRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
-
-      // |ASN1_STRFLGS_SHOW_TYPE| prepends the type name.
-      {V_ASN1_UTF8STRING, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "UTF8STRING:a"},
-      {-1, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "(unknown):a"},
-      {100, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "(unknown):a"},
-
-      // |ASN1_STRFLGS_DUMP_ALL| and |ASN1_STRFLGS_DUMP_UNKNOWN| cause
-      // non-string types to be printed in hex, though without the DER wrapper
-      // by default.
-      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
-       ASN1_STRFLGS_DUMP_UNKNOWN, "\\U2603"},
-      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
-       ASN1_STRFLGS_DUMP_ALL, "#E29883"},
-      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
-       ASN1_STRFLGS_DUMP_UNKNOWN, "#E29883"},
-      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
-       ASN1_STRFLGS_DUMP_ALL, "#E29883"},
-
-      // |ASN1_STRFLGS_DUMP_DER| includes the entire element.
-      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
-       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER, "#0C03E29883"},
-      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
-       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER, "#0403E29883"},
-      {V_ASN1_BIT_STRING,
-       {0x80},
-       ASN1_STRING_FLAG_BITS_LEFT | 4,
-       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
-       "#03020480"},
-      // INTEGER { 1 }
-      {V_ASN1_INTEGER,
-       {0x01},
-       0,
-       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
-       "#020101"},
-      // INTEGER { -1 }
-      {V_ASN1_NEG_INTEGER,
-       {0x01},
-       0,
-       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
-       "#0201FF"},
-      // ENUMERATED { 1 }
-      {V_ASN1_ENUMERATED,
-       {0x01},
-       0,
-       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
-       "#0A0101"},
-      // ENUMERATED { -1 }
-      {V_ASN1_NEG_ENUMERATED,
-       {0x01},
-       0,
-       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
-       "#0A01FF"},
-  };
-  for (const auto &t : kTests) {
-    SCOPED_TRACE(t.type);
-    SCOPED_TRACE(Bytes(t.data));
-    SCOPED_TRACE(t.str_flags);
-    SCOPED_TRACE(t.flags);
-
-    bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_type_new(t.type));
-    ASSERT_TRUE(ASN1_STRING_set(str.get(), t.data.data(), t.data.size()));
-    str->flags = t.str_flags;
-
-    // If the |BIO| is null, it should measure the size.
-    int len = ASN1_STRING_print_ex(nullptr, str.get(), t.flags);
-    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
-
-    // Measuring the size should also work for the |FILE| version
-    len = ASN1_STRING_print_ex_fp(nullptr, str.get(), t.flags);
-    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
-
-    // Actually print the string.
-    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
-    ASSERT_TRUE(bio);
-    len = ASN1_STRING_print_ex(bio.get(), str.get(), t.flags);
-    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
-
-    const uint8_t *bio_contents;
-    size_t bio_len;
-    ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_contents, &bio_len));
-    EXPECT_EQ(t.expected, std::string(bio_contents, bio_contents + bio_len));
-  }
-
-  const struct {
-    int type;
-    std::vector<uint8_t> data;
-    int str_flags;
-    unsigned long flags;
-  } kUnprintableTests[] = {
-      // When decoding strings, invalid codepoints are errors.
-      {V_ASN1_UTF8STRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
-      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
-      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
-      {V_ASN1_UNIVERSALSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
-  };
-  for (const auto &t : kUnprintableTests) {
-    SCOPED_TRACE(t.type);
-    SCOPED_TRACE(Bytes(t.data));
-    SCOPED_TRACE(t.str_flags);
-    SCOPED_TRACE(t.flags);
-
-    bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_type_new(t.type));
-    ASSERT_TRUE(ASN1_STRING_set(str.get(), t.data.data(), t.data.size()));
-    str->flags = t.str_flags;
-
-    // If the |BIO| is null, it should measure the size.
-    int len = ASN1_STRING_print_ex(nullptr, str.get(), t.flags);
-    EXPECT_EQ(len, -1);
-    ERR_clear_error();
-
-    // Measuring the size should also work for the |FILE| version
-    len = ASN1_STRING_print_ex_fp(nullptr, str.get(), t.flags);
-    EXPECT_EQ(len, -1);
-    ERR_clear_error();
-
-    // Actually print the string.
-    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
-    ASSERT_TRUE(bio);
-    len = ASN1_STRING_print_ex(bio.get(), str.get(), t.flags);
-    EXPECT_EQ(len, -1);
-    ERR_clear_error();
-  }
-}
-
-TEST(ASN1Test, MBString) {
-  const unsigned long kAll = B_ASN1_PRINTABLESTRING | B_ASN1_IA5STRING |
-                             B_ASN1_T61STRING | B_ASN1_BMPSTRING |
-                             B_ASN1_UNIVERSALSTRING | B_ASN1_UTF8STRING;
-
-  const struct {
-    int format;
-    std::vector<uint8_t> in;
-    unsigned long mask;
-    int expected_type;
-    std::vector<uint8_t> expected_data;
-    int num_codepoints;
-  } kTests[] = {
-      // Given a choice of formats, we pick the smallest that fits.
-      {MBSTRING_UTF8, {}, kAll, V_ASN1_PRINTABLESTRING, {}, 0},
-      {MBSTRING_UTF8, {'a'}, kAll, V_ASN1_PRINTABLESTRING, {'a'}, 1},
-      {MBSTRING_UTF8,
-       {'a', 'A', '0', '\'', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'},
-       kAll,
-       V_ASN1_PRINTABLESTRING,
-       {'a', 'A', '0', '\'', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'},
-       14},
-      {MBSTRING_UTF8, {'*'}, kAll, V_ASN1_IA5STRING, {'*'}, 1},
-      {MBSTRING_UTF8, {'\n'}, kAll, V_ASN1_IA5STRING, {'\n'}, 1},
-      {MBSTRING_UTF8,
-       {0xc2, 0x80 /* U+0080 */},
-       kAll,
-       V_ASN1_T61STRING,
-       {0x80},
-       1},
-      {MBSTRING_UTF8,
-       {0xc4, 0x80 /* U+0100 */},
-       kAll,
-       V_ASN1_BMPSTRING,
-       {0x01, 0x00},
-       1},
-      {MBSTRING_UTF8,
-       {0xf0, 0x90, 0x80, 0x80 /* U+10000 */},
-       kAll,
-       V_ASN1_UNIVERSALSTRING,
-       {0x00, 0x01, 0x00, 0x00},
-       1},
-      {MBSTRING_UTF8,
-       {0xf0, 0x90, 0x80, 0x80 /* U+10000 */},
-       kAll & ~B_ASN1_UNIVERSALSTRING,
-       V_ASN1_UTF8STRING,
-       {0xf0, 0x90, 0x80, 0x80},
-       1},
-
-      // NUL is not printable. It should also not terminate iteration.
-      {MBSTRING_UTF8, {0}, kAll, V_ASN1_IA5STRING, {0}, 1},
-      {MBSTRING_UTF8, {0, 'a'}, kAll, V_ASN1_IA5STRING, {0, 'a'}, 2},
-
-      // When a particular format is specified, we use it.
-      {MBSTRING_UTF8,
-       {'a'},
-       B_ASN1_PRINTABLESTRING,
-       V_ASN1_PRINTABLESTRING,
-       {'a'},
-       1},
-      {MBSTRING_UTF8, {'a'}, B_ASN1_IA5STRING, V_ASN1_IA5STRING, {'a'}, 1},
-      {MBSTRING_UTF8, {'a'}, B_ASN1_T61STRING, V_ASN1_T61STRING, {'a'}, 1},
-      {MBSTRING_UTF8, {'a'}, B_ASN1_UTF8STRING, V_ASN1_UTF8STRING, {'a'}, 1},
-      {MBSTRING_UTF8,
-       {'a'},
-       B_ASN1_BMPSTRING,
-       V_ASN1_BMPSTRING,
-       {0x00, 'a'},
-       1},
-      {MBSTRING_UTF8,
-       {'a'},
-       B_ASN1_UNIVERSALSTRING,
-       V_ASN1_UNIVERSALSTRING,
-       {0x00, 0x00, 0x00, 'a'},
-       1},
-
-      // A long string with characters of many widths, to test sizes are
-      // measured in code points.
-      {MBSTRING_UTF8,
-       {
-           'a',                     //
-           0xc2, 0x80,              // U+0080
-           0xc4, 0x80,              // U+0100
-           0xf0, 0x90, 0x80, 0x80,  // U+10000
-       },
-       B_ASN1_UNIVERSALSTRING,
-       V_ASN1_UNIVERSALSTRING,
-       {
-           0x00, 0x00, 0x00, 'a',   //
-           0x00, 0x00, 0x00, 0x80,  //
-           0x00, 0x00, 0x01, 0x00,  //
-           0x00, 0x01, 0x00, 0x00,  //
-       },
-       4},
-  };
-  for (const auto &t : kTests) {
-    SCOPED_TRACE(t.format);
-    SCOPED_TRACE(Bytes(t.in));
-    SCOPED_TRACE(t.mask);
-
-    // Passing in nullptr should do a dry run.
-    EXPECT_EQ(t.expected_type,
-              ASN1_mbstring_copy(nullptr, t.in.data(), t.in.size(), t.format,
-                                 t.mask));
-
-    // Test allocating a new object.
-    ASN1_STRING *str = nullptr;
-    EXPECT_EQ(
-        t.expected_type,
-        ASN1_mbstring_copy(&str, t.in.data(), t.in.size(), t.format, t.mask));
-    ASSERT_TRUE(str);
-    EXPECT_EQ(t.expected_type, ASN1_STRING_type(str));
-    EXPECT_EQ(Bytes(t.expected_data),
-              Bytes(ASN1_STRING_get0_data(str), ASN1_STRING_length(str)));
-
-    // Test writing into an existing object.
-    ASN1_STRING_free(str);
-    str = ASN1_STRING_new();
-    ASSERT_TRUE(str);
-    ASN1_STRING *old_str = str;
-    EXPECT_EQ(
-        t.expected_type,
-        ASN1_mbstring_copy(&str, t.in.data(), t.in.size(), t.format, t.mask));
-    ASSERT_EQ(old_str, str);
-    EXPECT_EQ(t.expected_type, ASN1_STRING_type(str));
-    EXPECT_EQ(Bytes(t.expected_data),
-              Bytes(ASN1_STRING_get0_data(str), ASN1_STRING_length(str)));
-    ASN1_STRING_free(str);
-    str = nullptr;
-
-    // minsize and maxsize should be enforced, even in a dry run.
-    EXPECT_EQ(t.expected_type,
-              ASN1_mbstring_ncopy(nullptr, t.in.data(), t.in.size(), t.format,
-                                  t.mask, /*minsize=*/t.num_codepoints,
-                                  /*maxsize=*/t.num_codepoints));
-
-    EXPECT_EQ(t.expected_type,
-              ASN1_mbstring_ncopy(&str, t.in.data(), t.in.size(), t.format,
-                                  t.mask, /*minsize=*/t.num_codepoints,
-                                  /*maxsize=*/t.num_codepoints));
-    ASSERT_TRUE(str);
-    EXPECT_EQ(t.expected_type, ASN1_STRING_type(str));
-    EXPECT_EQ(Bytes(t.expected_data),
-              Bytes(ASN1_STRING_get0_data(str), ASN1_STRING_length(str)));
-    ASN1_STRING_free(str);
-    str = nullptr;
-
-    EXPECT_EQ(-1, ASN1_mbstring_ncopy(
-                      nullptr, t.in.data(), t.in.size(), t.format, t.mask,
-                      /*minsize=*/t.num_codepoints + 1, /*maxsize=*/0));
-    ERR_clear_error();
-    EXPECT_EQ(-1, ASN1_mbstring_ncopy(
-                      &str, t.in.data(), t.in.size(), t.format, t.mask,
-                      /*minsize=*/t.num_codepoints + 1, /*maxsize=*/0));
-    EXPECT_FALSE(str);
-    ERR_clear_error();
-    if (t.num_codepoints > 1) {
-      EXPECT_EQ(-1, ASN1_mbstring_ncopy(
-                        nullptr, t.in.data(), t.in.size(), t.format, t.mask,
-                        /*minsize=*/0, /*maxsize=*/t.num_codepoints - 1));
-      ERR_clear_error();
-      EXPECT_EQ(-1, ASN1_mbstring_ncopy(
-                        &str, t.in.data(), t.in.size(), t.format, t.mask,
-                        /*minsize=*/0, /*maxsize=*/t.num_codepoints - 1));
-      EXPECT_FALSE(str);
-      ERR_clear_error();
-    }
-  }
-
-  const struct {
-    int format;
-    std::vector<uint8_t> in;
-    unsigned long mask;
-  } kInvalidTests[] = {
-      // Invalid encodings are rejected.
-      {MBSTRING_UTF8, {0xff}, B_ASN1_UTF8STRING},
-      {MBSTRING_BMP, {0xff}, B_ASN1_UTF8STRING},
-      {MBSTRING_UNIV, {0xff}, B_ASN1_UTF8STRING},
-
-      // Lone surrogates are not code points.
-      {MBSTRING_UTF8, {0xed, 0xa0, 0x80}, B_ASN1_UTF8STRING},
-      {MBSTRING_BMP, {0xd8, 0x00}, B_ASN1_UTF8STRING},
-      {MBSTRING_UNIV, {0x00, 0x00, 0xd8, 0x00}, B_ASN1_UTF8STRING},
-
-      // The input does not fit in the allowed output types.
-      {MBSTRING_UTF8, {'\n'}, B_ASN1_PRINTABLESTRING},
-      {MBSTRING_UTF8,
-       {0xc2, 0x80 /* U+0080 */},
-       B_ASN1_PRINTABLESTRING | B_ASN1_IA5STRING},
-      {MBSTRING_UTF8,
-       {0xc4, 0x80 /* U+0100 */},
-       B_ASN1_PRINTABLESTRING | B_ASN1_IA5STRING | B_ASN1_T61STRING},
-      {MBSTRING_UTF8,
-       {0xf0, 0x90, 0x80, 0x80 /* U+10000 */},
-       B_ASN1_PRINTABLESTRING | B_ASN1_IA5STRING | B_ASN1_T61STRING |
-           B_ASN1_BMPSTRING},
-
-      // Unrecognized bits are ignored.
-      {MBSTRING_UTF8, {'\n'}, B_ASN1_PRINTABLESTRING | B_ASN1_SEQUENCE},
-  };
-  for (const auto &t : kInvalidTests) {
-    SCOPED_TRACE(t.format);
-    SCOPED_TRACE(Bytes(t.in));
-    SCOPED_TRACE(t.mask);
-
-    EXPECT_EQ(-1, ASN1_mbstring_copy(nullptr, t.in.data(), t.in.size(),
-                                     t.format, t.mask));
-    ERR_clear_error();
-
-    ASN1_STRING *str = nullptr;
-    EXPECT_EQ(-1, ASN1_mbstring_copy(&str, t.in.data(), t.in.size(),
-                                     t.format, t.mask));
-    ERR_clear_error();
-    EXPECT_EQ(nullptr, str);
-  }
-}
-
-// Test that multi-string types correctly encode negative ENUMERATED.
-// Multi-string types cannot contain INTEGER, so we only test ENUMERATED.
-TEST(ASN1Test, NegativeEnumeratedMultistring) {
-  static const uint8_t kMinusOne[] = {0x0a, 0x01, 0xff};  // ENUMERATED { -1 }
-  // |ASN1_PRINTABLE| is a multi-string type that allows ENUMERATED.
-  const uint8_t *p = kMinusOne;
-  bssl::UniquePtr<ASN1_STRING> str(
-      d2i_ASN1_PRINTABLE(nullptr, &p, sizeof(kMinusOne)));
-  ASSERT_TRUE(str);
-  TestSerialize(str.get(), i2d_ASN1_PRINTABLE, kMinusOne);
-}
-
-TEST(ASN1Test, PrintableType) {
-  const struct {
-    std::vector<uint8_t> in;
-    int result;
-  } kTests[] = {
-      {{}, V_ASN1_PRINTABLESTRING},
-      {{'a', 'A', '0', '\'', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'},
-       V_ASN1_PRINTABLESTRING},
-      {{'*'}, V_ASN1_IA5STRING},
-      {{'\0'}, V_ASN1_IA5STRING},
-      {{'\0', 'a'}, V_ASN1_IA5STRING},
-      {{0, 1, 2, 3, 125, 126, 127}, V_ASN1_IA5STRING},
-      {{0, 1, 2, 3, 125, 126, 127, 128}, V_ASN1_T61STRING},
-      {{128, 0, 1, 2, 3, 125, 126, 127}, V_ASN1_T61STRING},
-  };
-  for (const auto &t : kTests) {
-    SCOPED_TRACE(Bytes(t.in));
-    EXPECT_EQ(t.result, ASN1_PRINTABLE_type(t.in.data(), t.in.size()));
-  }
-}
-
-// Encoding a CHOICE type with an invalid selector should fail.
-TEST(ASN1Test, InvalidChoice) {
-  bssl::UniquePtr<GENERAL_NAME> name(GENERAL_NAME_new());
-  ASSERT_TRUE(name);
-  // CHOICE types are initialized with an invalid selector.
-  EXPECT_EQ(-1, name->type);
-  // |name| should fail to encode.
-  EXPECT_EQ(-1, i2d_GENERAL_NAME(name.get(), nullptr));
-
-  // The error should be propagated through types containing |name|.
-  bssl::UniquePtr<GENERAL_NAMES> names(GENERAL_NAMES_new());
-  ASSERT_TRUE(names);
-  EXPECT_TRUE(bssl::PushToStack(names.get(), std::move(name)));
-  EXPECT_EQ(-1, i2d_GENERAL_NAMES(names.get(), nullptr));
-}
-
-// Encoding NID-only |ASN1_OBJECT|s should fail.
-TEST(ASN1Test, InvalidObject) {
-  EXPECT_EQ(-1, i2d_ASN1_OBJECT(OBJ_nid2obj(NID_kx_ecdhe), nullptr));
-
-  bssl::UniquePtr<X509_ALGOR> alg(X509_ALGOR_new());
-  ASSERT_TRUE(alg);
-  ASSERT_TRUE(X509_ALGOR_set0(alg.get(), OBJ_nid2obj(NID_kx_ecdhe),
-                              V_ASN1_UNDEF, nullptr));
-  EXPECT_EQ(-1, i2d_X509_ALGOR(alg.get(), nullptr));
-}
-
-// Encoding invalid |ASN1_TYPE|s should fail. |ASN1_TYPE|s are
-// default-initialized to an invalid type.
-TEST(ASN1Test, InvalidASN1Type) {
-  bssl::UniquePtr<ASN1_TYPE> obj(ASN1_TYPE_new());
-  ASSERT_TRUE(obj);
-  EXPECT_EQ(-1, obj->type);
-  EXPECT_EQ(-1, i2d_ASN1_TYPE(obj.get(), nullptr));
-}
-
-// Encoding invalid MSTRING types should fail. An MSTRING is a CHOICE of
-// string-like types. They are initialized to an invalid type.
-TEST(ASN1Test, InvalidMSTRING) {
-  bssl::UniquePtr<ASN1_STRING> obj(ASN1_TIME_new());
-  ASSERT_TRUE(obj);
-  EXPECT_EQ(-1, obj->type);
-  EXPECT_EQ(-1, i2d_ASN1_TIME(obj.get(), nullptr));
-
-  obj.reset(DIRECTORYSTRING_new());
-  ASSERT_TRUE(obj);
-  EXPECT_EQ(-1, obj->type);
-  EXPECT_EQ(-1, i2d_DIRECTORYSTRING(obj.get(), nullptr));
-}
-
 // The ASN.1 macros do not work on Windows shared library builds, where usage of
 // |OPENSSL_EXPORT| is a bit stricter.
 #if !defined(OPENSSL_WINDOWS) || !defined(BORINGSSL_SHARED_LIBRARY)
@@ -1131,7 +468,7 @@
 DECLARE_ASN1_FUNCTIONS(ASN1_LINKED_LIST)
 
 ASN1_SEQUENCE(ASN1_LINKED_LIST) = {
-    ASN1_OPT(ASN1_LINKED_LIST, next, ASN1_LINKED_LIST),
+  ASN1_OPT(ASN1_LINKED_LIST, next, ASN1_LINKED_LIST),
 } ASN1_SEQUENCE_END(ASN1_LINKED_LIST)
 
 IMPLEMENT_ASN1_FUNCTIONS(ASN1_LINKED_LIST)
@@ -1182,13 +519,15 @@
   ASN1_STRING *string;
 };
 
+// clang-format off
 DECLARE_ASN1_FUNCTIONS(IMPLICIT_CHOICE)
 
 ASN1_SEQUENCE(IMPLICIT_CHOICE) = {
-    ASN1_IMP(IMPLICIT_CHOICE, string, DIRECTORYSTRING, 0),
+  ASN1_IMP(IMPLICIT_CHOICE, string, DIRECTORYSTRING, 0)
 } ASN1_SEQUENCE_END(IMPLICIT_CHOICE)
 
 IMPLEMENT_ASN1_FUNCTIONS(IMPLICIT_CHOICE)
+// clang-format on
 
 // Test that the ASN.1 templates reject types with implicitly-tagged CHOICE
 // types.
@@ -1213,50 +552,4 @@
   EXPECT_EQ(nullptr, d2i_IMPLICIT_CHOICE(nullptr, &ptr, sizeof(kInput2)));
 }
 
-struct REQUIRED_FIELD {
-  ASN1_INTEGER *value;
-  ASN1_INTEGER *value_imp;
-  ASN1_INTEGER *value_exp;
-  STACK_OF(ASN1_INTEGER) *seq;
-  STACK_OF(ASN1_INTEGER) *seq_imp;
-  STACK_OF(ASN1_INTEGER) *seq_exp;
-};
-
-DECLARE_ASN1_FUNCTIONS(REQUIRED_FIELD)
-ASN1_SEQUENCE(REQUIRED_FIELD) = {
-    ASN1_SIMPLE(REQUIRED_FIELD, value, ASN1_INTEGER),
-    ASN1_IMP(REQUIRED_FIELD, value_imp, ASN1_INTEGER, 0),
-    ASN1_EXP(REQUIRED_FIELD, value_exp, ASN1_INTEGER, 1),
-    ASN1_SEQUENCE_OF(REQUIRED_FIELD, seq, ASN1_INTEGER),
-    ASN1_IMP_SEQUENCE_OF(REQUIRED_FIELD, seq_imp, ASN1_INTEGER, 2),
-    ASN1_EXP_SEQUENCE_OF(REQUIRED_FIELD, seq_exp, ASN1_INTEGER, 3),
-} ASN1_SEQUENCE_END(REQUIRED_FIELD)
-IMPLEMENT_ASN1_FUNCTIONS(REQUIRED_FIELD)
-
-// Test that structures with missing required fields cannot be serialized. Test
-// the full combination of tagging and SEQUENCE OF.
-TEST(ASN1Test, MissingRequiredField) {
-  EXPECT_EQ(-1, i2d_REQUIRED_FIELD(nullptr, nullptr));
-
-  std::unique_ptr<REQUIRED_FIELD, decltype(&REQUIRED_FIELD_free)> obj(
-      nullptr, REQUIRED_FIELD_free);
-  for (auto field : {&REQUIRED_FIELD::value, &REQUIRED_FIELD::value_imp,
-                     &REQUIRED_FIELD::value_exp}) {
-    obj.reset(REQUIRED_FIELD_new());
-    ASSERT_TRUE(obj);
-    ASN1_INTEGER_free((*obj).*field);
-    (*obj).*field = nullptr;
-    EXPECT_EQ(-1, i2d_REQUIRED_FIELD(obj.get(), nullptr));
-  }
-
-  for (auto field : {&REQUIRED_FIELD::seq, &REQUIRED_FIELD::seq_imp,
-                     &REQUIRED_FIELD::seq_exp}) {
-    obj.reset(REQUIRED_FIELD_new());
-    ASSERT_TRUE(obj);
-    sk_ASN1_INTEGER_pop_free((*obj).*field, ASN1_INTEGER_free);
-    (*obj).*field = nullptr;
-    EXPECT_EQ(-1, i2d_REQUIRED_FIELD(obj.get(), nullptr));
-  }
-}
-
 #endif  // !WINDOWS || !SHARED_LIBRARY
diff --git a/src/crypto/asn1/charmap.pl b/src/crypto/asn1/charmap.pl
index 117ed32..71bc7b8 100644
--- a/src/crypto/asn1/charmap.pl
+++ b/src/crypto/asn1/charmap.pl
@@ -62,17 +62,17 @@
 # Set up an array with the type of ASCII characters
 # Each set bit represents a character property.
 
-# RFC 2253 character properties
+# RFC2253 character properties
 my $RFC2253_ESC = 1;	# Character escaped with \
 my $ESC_CTRL	= 2;	# Escaped control character
-# These are used with RFC 1779 quoting using "
+# These are used with RFC1779 quoting using "
 my $NOESC_QUOTE	= 8;	# Not escaped if quoted
 my $PSTRING_CHAR = 0x10;	# Valid PrintableString character
 my $RFC2253_FIRST_ESC = 0x20; # Escaped with \ if first character
 my $RFC2253_LAST_ESC = 0x40;  # Escaped with \ if last character
 
 for($i = 0; $i < 128; $i++) {
-	# Set the RFC 2253 escape characters (control)
+	# Set the RFC2253 escape characters (control)
 	$arr[$i] = 0;
 	if(($i < 32) || ($i > 126)) {
 		$arr[$i] |= $ESC_CTRL;
@@ -88,7 +88,7 @@
 
 # Now setup the rest
 
-# Remaining RFC 2253 escaped characters
+# Remaining RFC2253 escaped characters
 
 $arr[ord(" ")] |= $NOESC_QUOTE | $RFC2253_FIRST_ESC | $RFC2253_LAST_ESC;
 $arr[ord("#")] |= $NOESC_QUOTE | $RFC2253_FIRST_ESC;
diff --git a/src/crypto/asn1/internal.h b/src/crypto/asn1/internal.h
index a4bd34e..4e42c70 100644
--- a/src/crypto/asn1/internal.h
+++ b/src/crypto/asn1/internal.h
@@ -123,31 +123,15 @@
                      const ASN1_ITEM *it, int tag, int aclass, char opt,
                      ASN1_TLC *ctx);
 
-/* ASN1_item_ex_i2d encodes |*pval| as a value of type |it| to |out| under the
- * i2d output convention. It returns a non-zero length on success and -1 on
- * error. If |tag| is -1. the tag and class come from |it|. Otherwise, the tag
- * number is |tag| and the class is |aclass|. This is used for implicit tagging.
- * This function treats a missing value as an error, not an optional field. */
 int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
                      const ASN1_ITEM *it, int tag, int aclass);
-
 void ASN1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
 
-/* asn1_get_choice_selector returns the CHOICE selector value for |*pval|, which
- * must of type |it|. */
 int asn1_get_choice_selector(ASN1_VALUE **pval, const ASN1_ITEM *it);
-
 int asn1_set_choice_selector(ASN1_VALUE **pval, int value, const ASN1_ITEM *it);
 
-/* asn1_get_field_ptr returns a pointer to the field in |*pval| corresponding to
- * |tt|. */
 ASN1_VALUE **asn1_get_field_ptr(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
 
-/* asn1_do_adb returns the |ASN1_TEMPLATE| for the ANY DEFINED BY field |tt|,
- * based on the selector INTEGER or OID in |*pval|. If |tt| is not an ADB field,
- * it returns |tt|. If the selector does not match any value, it returns NULL.
- * If |nullerr| is non-zero, it will additionally push an error to the error
- * queue when there is no match. */
 const ASN1_TEMPLATE *asn1_do_adb(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt,
                                  int nullerr);
 
@@ -156,13 +140,8 @@
 
 void asn1_enc_init(ASN1_VALUE **pval, const ASN1_ITEM *it);
 void asn1_enc_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
-
-/* asn1_enc_restore, if |*pval| has a saved encoding, writes it to |out| under
- * the i2d output convention, sets |*len| to the length, and returns one. If it
- * has no saved encoding, it returns zero. */
 int asn1_enc_restore(int *len, unsigned char **out, ASN1_VALUE **pval,
                      const ASN1_ITEM *it);
-
 int asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, int inlen,
                   const ASN1_ITEM *it);
 
@@ -171,10 +150,6 @@
  * a pointer. */
 const void *asn1_type_value_as_pointer(const ASN1_TYPE *a);
 
-/* asn1_is_printable returns one if |value| is a valid Unicode codepoint for an
- * ASN.1 PrintableString, and zero otherwise. */
-int asn1_is_printable(uint32_t value);
-
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/src/crypto/asn1/tasn_dec.c b/src/crypto/asn1/tasn_dec.c
index c5a6477..0d123cc 100644
--- a/src/crypto/asn1/tasn_dec.c
+++ b/src/crypto/asn1/tasn_dec.c
@@ -170,6 +170,8 @@
 {
     const ASN1_TEMPLATE *tt, *errtt = NULL;
     const ASN1_EXTERN_FUNCS *ef;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb;
     const unsigned char *p = NULL, *q;
     unsigned char oclass;
     char seq_eoc, seq_nolen, cst, isopt;
@@ -181,6 +183,10 @@
     aclass &= ~ASN1_TFLG_COMBINE;
     if (!pval)
         return 0;
+    if (aux && aux->asn1_cb)
+        asn1_cb = aux->asn1_cb;
+    else
+        asn1_cb = 0;
 
     /*
      * Bound |len| to comfortably fit in an int. Lengths in this module often
@@ -258,7 +264,7 @@
         ef = it->funcs;
         return ef->asn1_ex_d2i(pval, in, len, it, tag, aclass, opt, ctx);
 
-    case ASN1_ITYPE_CHOICE: {
+    case ASN1_ITYPE_CHOICE:
         /*
          * It never makes sense for CHOICE types to have implicit tagging, so if
          * tag != -1, then this looks like an error in the template.
@@ -268,8 +274,6 @@
             goto err;
         }
 
-        const ASN1_AUX *aux = it->funcs;
-        ASN1_aux_cb *asn1_cb = aux != NULL ? aux->asn1_cb : NULL;
         if (asn1_cb && !asn1_cb(ASN1_OP_D2I_PRE, pval, it, NULL))
             goto auxerr;
 
@@ -323,9 +327,8 @@
             goto auxerr;
         *in = p;
         return 1;
-    }
 
-    case ASN1_ITYPE_SEQUENCE: {
+    case ASN1_ITYPE_SEQUENCE:
         p = *in;
 
         /* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
@@ -353,8 +356,6 @@
             goto err;
         }
 
-        const ASN1_AUX *aux = it->funcs;
-        ASN1_aux_cb *asn1_cb = aux != NULL ? aux->asn1_cb : NULL;
         if (asn1_cb && !asn1_cb(ASN1_OP_D2I_PRE, pval, it, NULL))
             goto auxerr;
 
@@ -461,7 +462,6 @@
             goto auxerr;
         *in = p;
         return 1;
-    }
 
     default:
         return 0;
diff --git a/src/crypto/asn1/tasn_enc.c b/src/crypto/asn1/tasn_enc.c
index 9917d2a..3229a71 100644
--- a/src/crypto/asn1/tasn_enc.c
+++ b/src/crypto/asn1/tasn_enc.c
@@ -66,18 +66,17 @@
 #include "internal.h"
 
 
-static int asn1_item_ex_i2d_opt(ASN1_VALUE **pval, unsigned char **out,
-                                const ASN1_ITEM *it, int tag, int aclass,
-                                int optional);
 static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
-                                 const ASN1_ITEM *it, int tag, int aclass,
-                                 int optional);
-static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cont, int *out_omit,
-                       int *putype, const ASN1_ITEM *it);
+                                 const ASN1_ITEM *it, int tag, int aclass);
+static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
+                       const ASN1_ITEM *it);
 static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
-                            int skcontlen, const ASN1_ITEM *item, int do_sort);
+                            int skcontlen, const ASN1_ITEM *item,
+                            int do_sort, int iclass);
 static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
                                 const ASN1_TEMPLATE *tt, int tag, int aclass);
+static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out,
+                               const ASN1_ITEM *it, int flags);
 
 /*
  * Top level i2d equivalents
@@ -85,28 +84,35 @@
 
 int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **out, const ASN1_ITEM *it)
 {
+    return asn1_item_flags_i2d(val, out, it, 0);
+}
+
+/*
+ * Encode an ASN1 item, this is use by the standard 'i2d' function. 'out'
+ * points to a buffer to output the data to. The new i2d has one additional
+ * feature. If the output buffer is NULL (i.e. *out == NULL) then a buffer is
+ * allocated and populated with the encoding.
+ */
+
+static int asn1_item_flags_i2d(ASN1_VALUE *val, unsigned char **out,
+                               const ASN1_ITEM *it, int flags)
+{
     if (out && !*out) {
         unsigned char *p, *buf;
-        int len = ASN1_item_ex_i2d(&val, NULL, it, /*tag=*/-1, /*aclass=*/0);
-        if (len <= 0) {
+        int len;
+        len = ASN1_item_ex_i2d(&val, NULL, it, -1, flags);
+        if (len <= 0)
             return len;
-        }
         buf = OPENSSL_malloc(len);
-        if (!buf) {
-            OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
+        if (!buf)
             return -1;
-        }
         p = buf;
-        int len2 = ASN1_item_ex_i2d(&val, &p, it, /*tag=*/-1, /*aclass=*/0);
-        if (len2 <= 0) {
-            return len2;
-        }
-        assert(len == len2);
+        ASN1_item_ex_i2d(&val, &p, it, -1, flags);
         *out = buf;
         return len;
     }
 
-    return ASN1_item_ex_i2d(&val, out, it, /*tag=*/-1, /*aclass=*/0);
+    return ASN1_item_ex_i2d(&val, out, it, -1, flags);
 }
 
 /*
@@ -117,47 +123,26 @@
 int ASN1_item_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
                      const ASN1_ITEM *it, int tag, int aclass)
 {
-    int ret = asn1_item_ex_i2d_opt(pval, out, it, tag, aclass, /*optional=*/0);
-    assert(ret != 0);
-    return ret;
-}
-
-/* asn1_item_ex_i2d_opt behaves like |ASN1_item_ex_i2d| but, if |optional| is
- * non-zero and |*pval| is omitted, it returns zero and writes no bytes. */
-int asn1_item_ex_i2d_opt(ASN1_VALUE **pval, unsigned char **out,
-                         const ASN1_ITEM *it, int tag, int aclass,
-                         int optional)
-{
     const ASN1_TEMPLATE *tt = NULL;
     int i, seqcontlen, seqlen;
+    const ASN1_EXTERN_FUNCS *ef;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb = 0;
 
-    /* Historically, |aclass| was repurposed to pass additional flags into the
-     * encoding process. */
-    assert((aclass & ASN1_TFLG_TAG_CLASS) == aclass);
-    /* If not overridding the tag, |aclass| is ignored and should be zero. */
-    assert(tag != -1 || aclass == 0);
+    if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval)
+        return 0;
 
-    /* All fields are pointers, except for boolean |ASN1_ITYPE_PRIMITIVE|s.
-     * Optional primitives are handled later. */
-    if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval) {
-        if (optional) {
-            return 0;
-        }
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
-        return -1;
-    }
+    if (aux && aux->asn1_cb)
+        asn1_cb = aux->asn1_cb;
 
     switch (it->itype) {
 
     case ASN1_ITYPE_PRIMITIVE:
-        if (it->templates) {
-            if (it->templates->flags & ASN1_TFLG_OPTIONAL) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
-                return -1;
-            }
-            return asn1_template_ex_i2d(pval, out, it->templates, tag, aclass);
-        }
-        return asn1_i2d_ex_primitive(pval, out, it, tag, aclass, optional);
+        if (it->templates)
+            return asn1_template_ex_i2d(pval, out, it->templates,
+                                        tag, aclass);
+        return asn1_i2d_ex_primitive(pval, out, it, tag, aclass);
+        break;
 
     case ASN1_ITYPE_MSTRING:
         /*
@@ -168,9 +153,9 @@
             OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
             return -1;
         }
-        return asn1_i2d_ex_primitive(pval, out, it, -1, 0, optional);
+        return asn1_i2d_ex_primitive(pval, out, it, -1, aclass);
 
-    case ASN1_ITYPE_CHOICE: {
+    case ASN1_ITYPE_CHOICE:
         /*
          * It never makes sense for CHOICE types to have implicit tagging, so if
          * tag != -1, then this looks like an error in the template.
@@ -179,39 +164,31 @@
             OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
             return -1;
         }
+        if (asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it, NULL))
+            return 0;
         i = asn1_get_choice_selector(pval, it);
-        if (i < 0 || i >= it->tcount) {
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_NO_MATCHING_CHOICE_TYPE);
-            return -1;
+        if ((i >= 0) && (i < it->tcount)) {
+            ASN1_VALUE **pchval;
+            const ASN1_TEMPLATE *chtt;
+            chtt = it->templates + i;
+            pchval = asn1_get_field_ptr(pval, chtt);
+            return asn1_template_ex_i2d(pchval, out, chtt, -1, aclass);
         }
-        const ASN1_TEMPLATE *chtt = it->templates + i;
-        if (chtt->flags & ASN1_TFLG_OPTIONAL) {
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
-            return -1;
-        }
-        ASN1_VALUE **pchval = asn1_get_field_ptr(pval, chtt);
-        return asn1_template_ex_i2d(pchval, out, chtt, -1, 0);
-    }
+        /* Fixme: error condition if selector out of range */
+        if (asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it, NULL))
+            return 0;
+        break;
 
-    case ASN1_ITYPE_EXTERN: {
+    case ASN1_ITYPE_EXTERN:
         /* If new style i2d it does all the work */
-        const ASN1_EXTERN_FUNCS *ef = it->funcs;
-        int ret = ef->asn1_ex_i2d(pval, out, it, tag, aclass);
-        if (ret == 0) {
-            /* |asn1_ex_i2d| should never return zero. We have already checked
-             * for optional values generically, and |ASN1_ITYPE_EXTERN| fields
-             * must be pointers. */
-            OPENSSL_PUT_ERROR(ASN1, ERR_R_INTERNAL_ERROR);
-            return -1;
-        }
-        return ret;
-    }
+        ef = it->funcs;
+        return ef->asn1_ex_i2d(pval, out, it, tag, aclass);
 
-    case ASN1_ITYPE_SEQUENCE: {
+    case ASN1_ITYPE_SEQUENCE:
         i = asn1_enc_restore(&seqcontlen, out, pval, it);
         /* An error occurred */
         if (i < 0)
-            return -1;
+            return 0;
         /* We have a valid cached encoding... */
         if (i > 0)
             return seqcontlen;
@@ -220,8 +197,12 @@
         /* If no IMPLICIT tagging set to SEQUENCE, UNIVERSAL */
         if (tag == -1) {
             tag = V_ASN1_SEQUENCE;
-            aclass = V_ASN1_UNIVERSAL;
+            /* Retain any other flags in aclass */
+            aclass = (aclass & ~ASN1_TFLG_TAG_CLASS)
+                | V_ASN1_UNIVERSAL;
         }
+        if (asn1_cb && !asn1_cb(ASN1_OP_I2D_PRE, pval, it, NULL))
+            return 0;
         /* First work out sequence content length */
         for (i = 0, tt = it->templates; i < it->tcount; tt++, i++) {
             const ASN1_TEMPLATE *seqtt;
@@ -229,9 +210,9 @@
             int tmplen;
             seqtt = asn1_do_adb(pval, tt, 1);
             if (!seqtt)
-                return -1;
+                return 0;
             pseqval = asn1_get_field_ptr(pval, seqtt);
-            tmplen = asn1_template_ex_i2d(pseqval, NULL, seqtt, -1, 0);
+            tmplen = asn1_template_ex_i2d(pseqval, NULL, seqtt, -1, aclass);
             if (tmplen == -1 || (tmplen > INT_MAX - seqcontlen))
                 return -1;
             seqcontlen += tmplen;
@@ -247,49 +228,40 @@
             ASN1_VALUE **pseqval;
             seqtt = asn1_do_adb(pval, tt, 1);
             if (!seqtt)
-                return -1;
+                return 0;
             pseqval = asn1_get_field_ptr(pval, seqtt);
-            if (asn1_template_ex_i2d(pseqval, out, seqtt, -1, 0) < 0) {
-                return -1;
-            }
+            /* FIXME: check for errors in enhanced version */
+            asn1_template_ex_i2d(pseqval, out, seqtt, -1, aclass);
         }
+        if (asn1_cb && !asn1_cb(ASN1_OP_I2D_POST, pval, it, NULL))
+            return 0;
         return seqlen;
-    }
 
     default:
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
-        return -1;
+        return 0;
+
     }
+    return 0;
 }
 
-/* asn1_template_ex_i2d behaves like |asn1_item_ex_i2d_opt| but uses an
- * |ASN1_TEMPLATE| instead of an |ASN1_ITEM|. An |ASN1_TEMPLATE| wraps an
- * |ASN1_ITEM| with modifiers such as tagging, SEQUENCE or SET, etc. Instead of
- * taking an |optional| parameter, it uses the |ASN1_TFLG_OPTIONAL| flag. */
 static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out,
                                 const ASN1_TEMPLATE *tt, int tag, int iclass)
 {
     int i, ret, flags, ttag, tclass;
     size_t j;
     flags = tt->flags;
-
-    /* Historically, |iclass| was repurposed to pass additional flags into the
-     * encoding process. */
-    assert((iclass & ASN1_TFLG_TAG_CLASS) == iclass);
-    /* If not overridding the tag, |iclass| is ignored and should be zero. */
-    assert(tag != -1 || iclass == 0);
-
     /*
      * Work out tag and class to use: tagging may come either from the
      * template or the arguments, not both because this would create
-     * ambiguity.
+     * ambiguity. Additionally the iclass argument may contain some
+     * additional flags which should be noted and passed down to other
+     * levels.
      */
     if (flags & ASN1_TFLG_TAG_MASK) {
         /* Error if argument and template tagging */
-        if (tag != -1) {
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
+        if (tag != -1)
+            /* FIXME: error code here */
             return -1;
-        }
         /* Get tagging from template */
         ttag = tt->tag;
         tclass = flags & ASN1_TFLG_TAG_CLASS;
@@ -301,12 +273,14 @@
         ttag = -1;
         tclass = 0;
     }
-
-    const int optional = (flags & ASN1_TFLG_OPTIONAL) != 0;
+    /*
+     * Remove any class mask from iflag.
+     */
+    iclass &= ~ASN1_TFLG_TAG_CLASS;
 
     /*
-     * At this point 'ttag' contains the outer tag to use, and 'tclass' is the
-     * class.
+     * At this point 'ttag' contains the outer tag to use, 'tclass' is the
+     * class and iclass is any flags passed to this function.
      */
 
     if (flags & ASN1_TFLG_SK_MASK) {
@@ -316,22 +290,16 @@
         int skcontlen, sklen;
         ASN1_VALUE *skitem;
 
-        if (!*pval) {
-            if (optional) {
-                return 0;
-            }
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
-            return -1;
-        }
+        if (!*pval)
+            return 0;
 
         if (flags & ASN1_TFLG_SET_OF) {
             isset = 1;
-            /* Historically, types with both bits set were mutated when
-             * serialized to apply the sort. We no longer support this. */
-            assert((flags & ASN1_TFLG_SEQUENCE_OF) == 0);
-        } else {
+            /* 2 means we reorder */
+            if (flags & ASN1_TFLG_SEQUENCE_OF)
+                isset = 2;
+        } else
             isset = 0;
-        }
 
         /*
          * Work out inner tag value: if EXPLICIT or no tagging use underlying
@@ -354,7 +322,7 @@
             int tmplen;
             skitem = sk_ASN1_VALUE_value(sk, j);
             tmplen = ASN1_item_ex_i2d(&skitem, NULL, ASN1_ITEM_ptr(tt->item),
-                                      -1, 0);
+                                      -1, iclass);
             if (tmplen == -1 || (skcontlen > INT_MAX - tmplen))
                 return -1;
             skcontlen += tmplen;
@@ -378,36 +346,30 @@
         /* SET or SEQUENCE and IMPLICIT tag */
         ASN1_put_object(out, /*constructed=*/1, skcontlen, sktag, skaclass);
         /* And the stuff itself */
-        if (!asn1_set_seq_out(sk, out, skcontlen, ASN1_ITEM_ptr(tt->item),
-                              isset)) {
-            return -1;
-        }
+        asn1_set_seq_out(sk, out, skcontlen, ASN1_ITEM_ptr(tt->item),
+                         isset, iclass);
         return ret;
     }
 
     if (flags & ASN1_TFLG_EXPTAG) {
         /* EXPLICIT tagging */
         /* Find length of tagged item */
-        i = asn1_item_ex_i2d_opt(pval, NULL, ASN1_ITEM_ptr(tt->item), -1, 0,
-                                 optional);
-        if (i <= 0)
-            return i;
+        i = ASN1_item_ex_i2d(pval, NULL, ASN1_ITEM_ptr(tt->item), -1, iclass);
+        if (!i)
+            return 0;
         /* Find length of EXPLICIT tag */
         ret = ASN1_object_size(/*constructed=*/1, i, ttag);
         if (out && ret != -1) {
             /* Output tag and item */
             ASN1_put_object(out, /*constructed=*/1, i, ttag, tclass);
-            if (ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), -1,
-                                 0) < 0) {
-                return -1;
-            }
+            ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item), -1, iclass);
         }
         return ret;
     }
 
-    /* Either normal or IMPLICIT tagging */
-    return asn1_item_ex_i2d_opt(pval, out, ASN1_ITEM_ptr(tt->item),
-                                ttag, tclass, optional);
+    /* Either normal or IMPLICIT tagging: combine class and flags */
+    return ASN1_item_ex_i2d(pval, out, ASN1_ITEM_ptr(tt->item),
+                            ttag, tclass | iclass);
 
 }
 
@@ -416,6 +378,7 @@
 typedef struct {
     unsigned char *data;
     int length;
+    ASN1_VALUE *field;
 } DER_ENC;
 
 static int der_cmp(const void *a, const void *b)
@@ -429,96 +392,99 @@
     return d1->length - d2->length;
 }
 
-/* asn1_set_seq_out writes |sk| to |out| under the i2d output convention,
- * excluding the tag and length. It returns one on success and zero on error.
- * |skcontlen| must be the total encoded size. If |do_sort| is non-zero, the
- * elements are sorted for a SET OF type. Each element of |sk| has type
- * |item|. */
+/* Output the content octets of SET OF or SEQUENCE OF */
+
 static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out,
-                            int skcontlen, const ASN1_ITEM *item, int do_sort)
+                            int skcontlen, const ASN1_ITEM *item,
+                            int do_sort, int iclass)
 {
-    /* No need to sort if there are fewer than two items. */
-    if (!do_sort || sk_ASN1_VALUE_num(sk) < 2) {
-        for (size_t i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
-            ASN1_VALUE *skitem = sk_ASN1_VALUE_value(sk, i);
-            if (ASN1_item_ex_i2d(&skitem, out, item, -1, 0) < 0) {
+    size_t i;
+    ASN1_VALUE *skitem;
+    unsigned char *tmpdat = NULL, *p = NULL;
+    DER_ENC *derlst = NULL, *tder;
+    if (do_sort) {
+        /* Don't need to sort less than 2 items */
+        if (sk_ASN1_VALUE_num(sk) < 2)
+            do_sort = 0;
+        else {
+            derlst = OPENSSL_malloc(sk_ASN1_VALUE_num(sk)
+                                    * sizeof(*derlst));
+            if (!derlst)
+                return 0;
+            tmpdat = OPENSSL_malloc(skcontlen);
+            if (!tmpdat) {
+                OPENSSL_free(derlst);
                 return 0;
             }
         }
+    }
+    /* If not sorting just output each item */
+    if (!do_sort) {
+        for (i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
+            skitem = sk_ASN1_VALUE_value(sk, i);
+            ASN1_item_ex_i2d(&skitem, out, item, -1, iclass);
+        }
         return 1;
     }
+    p = tmpdat;
 
-    if (sk_ASN1_VALUE_num(sk) > ((size_t)-1) / sizeof(DER_ENC)) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_OVERFLOW);
-        return 0;
+    /* Doing sort: build up a list of each member's DER encoding */
+    for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++) {
+        skitem = sk_ASN1_VALUE_value(sk, i);
+        tder->data = p;
+        tder->length = ASN1_item_ex_i2d(&skitem, &p, item, -1, iclass);
+        tder->field = skitem;
     }
 
-    int ret = 0;
-    unsigned char *const buf = OPENSSL_malloc(skcontlen);
-    DER_ENC *encoded = OPENSSL_malloc(sk_ASN1_VALUE_num(sk) * sizeof(*encoded));
-    if (encoded == NULL || buf == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        goto err;
-    }
-
-    /* Encode all the elements into |buf| and populate |encoded|. */
-    unsigned char *p = buf;
-    for (size_t i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
-        ASN1_VALUE *skitem = sk_ASN1_VALUE_value(sk, i);
-        encoded[i].data = p;
-        encoded[i].length = ASN1_item_ex_i2d(&skitem, &p, item, -1, 0);
-        if (encoded[i].length < 0) {
-            goto err;
-        }
-        assert(p - buf <= skcontlen);
-    }
-
-    qsort(encoded, sk_ASN1_VALUE_num(sk), sizeof(*encoded), der_cmp);
-
-    /* Output the elements in sorted order. */
+    /* Now sort them */
+    qsort(derlst, sk_ASN1_VALUE_num(sk), sizeof(*derlst), der_cmp);
+    /* Output sorted DER encoding */
     p = *out;
-    for (size_t i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
-        OPENSSL_memcpy(p, encoded[i].data, encoded[i].length);
-        p += encoded[i].length;
+    for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++) {
+        OPENSSL_memcpy(p, tder->data, tder->length);
+        p += tder->length;
     }
     *out = p;
-
-    ret = 1;
-
-err:
-    OPENSSL_free(encoded);
-    OPENSSL_free(buf);
-    return ret;
+    /* If do_sort is 2 then reorder the STACK */
+    if (do_sort == 2) {
+        for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++)
+            (void)sk_ASN1_VALUE_set(sk, i, tder->field);
+    }
+    OPENSSL_free(derlst);
+    OPENSSL_free(tmpdat);
+    return 1;
 }
 
-/* asn1_i2d_ex_primitive behaves like |ASN1_item_ex_i2d| but |item| must be a
- * a PRIMITIVE or MSTRING type that is not an |ASN1_ITEM_TEMPLATE|. */
 static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
-                                 const ASN1_ITEM *it, int tag, int aclass,
-                                 int optional)
+                                 const ASN1_ITEM *it, int tag, int aclass)
 {
-    /* Get length of content octets and maybe find out the underlying type. */
-    int omit;
-    int utype = it->utype;
-    int len = asn1_ex_i2c(pval, NULL, &omit, &utype, it);
-    if (len < 0) {
-        return -1;
-    }
-    if (omit) {
-        if (optional) {
-            return 0;
-        }
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
-        return -1;
-    }
+    int len;
+    int utype;
+    int usetag;
+
+    utype = it->utype;
+
+    /*
+     * Get length of content octets and maybe find out the underlying type.
+     */
+
+    len = asn1_ex_i2c(pval, NULL, &utype, it);
 
     /*
      * If SEQUENCE, SET or OTHER then header is included in pseudo content
      * octets so don't include tag+length. We need to check here because the
      * call to asn1_ex_i2c() could change utype.
      */
-    int usetag = utype != V_ASN1_SEQUENCE && utype != V_ASN1_SET &&
-                 utype != V_ASN1_OTHER;
+    if ((utype == V_ASN1_SEQUENCE) || (utype == V_ASN1_SET) ||
+        (utype == V_ASN1_OTHER))
+        usetag = 0;
+    else
+        usetag = 1;
+
+    /* -1 means omit type */
+
+    if (len == -1)
+        return 0;
 
     /* If not implicitly tagged get tag from underlying type */
     if (tag == -1)
@@ -526,42 +492,21 @@
 
     /* Output tag+length followed by content octets */
     if (out) {
-        if (usetag) {
+        if (usetag)
             ASN1_put_object(out, /*constructed=*/0, len, tag, aclass);
-        }
-        int len2 = asn1_ex_i2c(pval, *out, &omit, &utype, it);
-        if (len2 < 0) {
-          return -1;
-        }
-        assert(len == len2);
-        assert(!omit);
+        asn1_ex_i2c(pval, *out, &utype, it);
         *out += len;
     }
 
-    if (usetag) {
+    if (usetag)
         return ASN1_object_size(/*constructed=*/0, len, tag);
-    }
     return len;
 }
 
-/* asn1_ex_i2c writes the |*pval| to |cout| under the i2d output convention,
- * excluding the tag and length. It returns the number of bytes written,
- * possibly zero, on success or -1 on error. If |*pval| should be omitted, it
- * returns zero and sets |*out_omit| to true.
- *
- * If |it| is an MSTRING or ANY type, it gets the underlying type from |*pval|,
- * which must be an |ASN1_STRING| or |ASN1_TYPE|, respectively. It then updates
- * |*putype| with the tag number of type used, or |V_ASN1_OTHER| if it was not a
- * universal type. If |*putype| is set to |V_ASN1_SEQUENCE|, |V_ASN1_SET|, or
- * |V_ASN1_OTHER|, it additionally outputs the tag and length, so the caller
- * must not do so.
- *
- * Otherwise, |*putype| must contain |it->utype|.
- *
- * WARNING: Unlike most functions in this file, |asn1_ex_i2c| can return zero
- * without omitting the element. ASN.1 values may have empty contents. */
-static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *out_omit,
-                       int *putype, const ASN1_ITEM *it)
+/* Produce content octets from a structure */
+
+static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *putype,
+                       const ASN1_ITEM *it)
 {
     ASN1_BOOLEAN *tbool = NULL;
     ASN1_STRING *strtmp;
@@ -575,51 +520,23 @@
      * |ASN1_PRIMITIVE_FUNCS| table of callbacks. */
     assert(it->funcs == NULL);
 
-    *out_omit = 0;
-
     /* Should type be omitted? */
     if ((it->itype != ASN1_ITYPE_PRIMITIVE)
         || (it->utype != V_ASN1_BOOLEAN)) {
-        if (!*pval) {
-            *out_omit = 1;
-            return 0;
-        }
+        if (!*pval)
+            return -1;
     }
 
     if (it->itype == ASN1_ITYPE_MSTRING) {
         /* If MSTRING type set the underlying type */
         strtmp = (ASN1_STRING *)*pval;
         utype = strtmp->type;
-        if (utype < 0 && utype != V_ASN1_OTHER) {
-            /* MSTRINGs can have type -1 when default-constructed. */
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
-            return -1;
-        }
-        /* Negative INTEGER and ENUMERATED values use |ASN1_STRING| type values
-         * that do not match their corresponding utype values. INTEGERs cannot
-         * participate in MSTRING types, but ENUMERATEDs can.
-         *
-         * TODO(davidben): Is this a bug? Although arguably one of the MSTRING
-         * types should contain more values, rather than less. See
-         * https://crbug.com/boringssl/412. But it is not possible to fit all
-         * possible ANY values into an |ASN1_STRING|, so matching the spec here
-         * is somewhat hopeless. */
-        if (utype == V_ASN1_NEG_INTEGER) {
-            utype = V_ASN1_INTEGER;
-        } else if (utype == V_ASN1_NEG_ENUMERATED) {
-            utype = V_ASN1_ENUMERATED;
-        }
         *putype = utype;
     } else if (it->utype == V_ASN1_ANY) {
         /* If ANY set type and pointer to value */
         ASN1_TYPE *typ;
         typ = (ASN1_TYPE *)*pval;
         utype = typ->type;
-        if (utype < 0 && utype != V_ASN1_OTHER) {
-            /* |ASN1_TYPE|s can have type -1 when default-constructed. */
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
-            return -1;
-        }
         *putype = utype;
         pval = &typ->value.asn1_value;
     } else
@@ -630,11 +547,8 @@
         otmp = (ASN1_OBJECT *)*pval;
         cont = otmp->data;
         len = otmp->length;
-        if (len == 0) {
-            /* Some |ASN1_OBJECT|s do not have OIDs and cannot be serialized. */
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_OBJECT);
+        if (cont == NULL || len == 0)
             return -1;
-        }
         break;
 
     case V_ASN1_NULL:
@@ -644,39 +558,34 @@
 
     case V_ASN1_BOOLEAN:
         tbool = (ASN1_BOOLEAN *)pval;
-        if (*tbool == -1) {
-            *out_omit = 1;
-            return 0;
-        }
+        if (*tbool == -1)
+            return -1;
         if (it->utype != V_ASN1_ANY) {
             /*
              * Default handling if value == size field then omit
              */
-            if ((*tbool && (it->size > 0)) ||
-                (!*tbool && !it->size)) {
-                *out_omit = 1;
-                return 0;
-            }
+            if (*tbool && (it->size > 0))
+                return -1;
+            if (!*tbool && !it->size)
+                return -1;
         }
         c = *tbool ? 0xff : 0x00;
         cont = &c;
         len = 1;
         break;
 
-    case V_ASN1_BIT_STRING: {
-        int ret = i2c_ASN1_BIT_STRING((ASN1_BIT_STRING *)*pval,
-                                      cout ? &cout : NULL);
-        /* |i2c_ASN1_BIT_STRING| returns zero on error instead of -1. */
-        return ret <= 0 ? -1 : ret;
-    }
+    case V_ASN1_BIT_STRING:
+        return i2c_ASN1_BIT_STRING((ASN1_BIT_STRING *)*pval,
+                                   cout ? &cout : NULL);
+        break;
 
     case V_ASN1_INTEGER:
-    case V_ASN1_ENUMERATED: {
-        /* |i2c_ASN1_INTEGER| also handles ENUMERATED. */
-        int ret = i2c_ASN1_INTEGER((ASN1_INTEGER *)*pval, cout ? &cout : NULL);
-        /* |i2c_ASN1_INTEGER| returns zero on error instead of -1. */
-        return ret <= 0 ? -1 : ret;
-    }
+    case V_ASN1_ENUMERATED:
+        /*
+         * These are all have the same content format as ASN1_INTEGER
+         */
+        return i2c_ASN1_INTEGER((ASN1_INTEGER *)*pval, cout ? &cout : NULL);
+        break;
 
     case V_ASN1_OCTET_STRING:
     case V_ASN1_NUMERICSTRING:
diff --git a/src/crypto/asn1/tasn_fre.c b/src/crypto/asn1/tasn_fre.c
index b847901..2f5032d 100644
--- a/src/crypto/asn1/tasn_fre.c
+++ b/src/crypto/asn1/tasn_fre.c
@@ -79,11 +79,17 @@
 {
     const ASN1_TEMPLATE *tt = NULL, *seqtt;
     const ASN1_EXTERN_FUNCS *ef;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb;
     int i;
     if (!pval)
         return;
     if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !*pval)
         return;
+    if (aux && aux->asn1_cb)
+        asn1_cb = aux->asn1_cb;
+    else
+        asn1_cb = 0;
 
     switch (it->itype) {
 
@@ -98,9 +104,7 @@
         ASN1_primitive_free(pval, it);
         break;
 
-    case ASN1_ITYPE_CHOICE: {
-        const ASN1_AUX *aux = it->funcs;
-        ASN1_aux_cb *asn1_cb = aux != NULL ? aux->asn1_cb : NULL;
+    case ASN1_ITYPE_CHOICE:
         if (asn1_cb) {
             i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
             if (i == 2)
@@ -120,7 +124,6 @@
             *pval = NULL;
         }
         break;
-    }
 
     case ASN1_ITYPE_EXTERN:
         ef = it->funcs;
@@ -128,11 +131,9 @@
             ef->asn1_ex_free(pval, it);
         break;
 
-    case ASN1_ITYPE_SEQUENCE: {
+    case ASN1_ITYPE_SEQUENCE:
         if (!asn1_refcount_dec_and_test_zero(pval, it))
             return;
-        const ASN1_AUX *aux = it->funcs;
-        ASN1_aux_cb *asn1_cb = aux != NULL ? aux->asn1_cb : NULL;
         if (asn1_cb) {
             i = asn1_cb(ASN1_OP_FREE_PRE, pval, it, NULL);
             if (i == 2)
@@ -161,7 +162,6 @@
         }
         break;
     }
-    }
 }
 
 void ASN1_template_free(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt)
diff --git a/src/crypto/asn1/tasn_new.c b/src/crypto/asn1/tasn_new.c
index eea219d..540ed8f 100644
--- a/src/crypto/asn1/tasn_new.c
+++ b/src/crypto/asn1/tasn_new.c
@@ -95,8 +95,14 @@
 {
     const ASN1_TEMPLATE *tt = NULL;
     const ASN1_EXTERN_FUNCS *ef;
+    const ASN1_AUX *aux = it->funcs;
+    ASN1_aux_cb *asn1_cb;
     ASN1_VALUE **pseqval;
     int i;
+    if (aux && aux->asn1_cb)
+        asn1_cb = aux->asn1_cb;
+    else
+        asn1_cb = 0;
 
     switch (it->itype) {
 
@@ -121,9 +127,7 @@
             goto memerr;
         break;
 
-    case ASN1_ITYPE_CHOICE: {
-        const ASN1_AUX *aux = it->funcs;
-        ASN1_aux_cb *asn1_cb = aux != NULL ? aux->asn1_cb : NULL;
+    case ASN1_ITYPE_CHOICE:
         if (asn1_cb) {
             i = asn1_cb(ASN1_OP_NEW_PRE, pval, it, NULL);
             if (!i)
@@ -142,11 +146,8 @@
         if (asn1_cb && !asn1_cb(ASN1_OP_NEW_POST, pval, it, NULL))
             goto auxerr2;
         break;
-    }
 
-    case ASN1_ITYPE_SEQUENCE: {
-        const ASN1_AUX *aux = it->funcs;
-        ASN1_aux_cb *asn1_cb = aux != NULL ? aux->asn1_cb : NULL;
+    case ASN1_ITYPE_SEQUENCE:
         if (asn1_cb) {
             i = asn1_cb(ASN1_OP_NEW_PRE, pval, it, NULL);
             if (!i)
@@ -172,7 +173,6 @@
             goto auxerr2;
         break;
     }
-    }
     return 1;
 
  memerr2:
@@ -271,6 +271,7 @@
 static int ASN1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
 {
     ASN1_TYPE *typ;
+    ASN1_STRING *str;
     int utype;
 
     if (!it)
@@ -307,7 +308,10 @@
         break;
 
     default:
-        *pval = (ASN1_VALUE *)ASN1_STRING_type_new(utype);
+        str = ASN1_STRING_type_new(utype);
+        if (it->itype == ASN1_ITYPE_MSTRING && str)
+            str->flags |= ASN1_STRING_FLAG_MSTRING;
+        *pval = (ASN1_VALUE *)str;
         break;
     }
     if (*pval)
diff --git a/src/crypto/asn1/tasn_utl.c b/src/crypto/asn1/tasn_utl.c
index 9b1da0b..24ad8c3 100644
--- a/src/crypto/asn1/tasn_utl.c
+++ b/src/crypto/asn1/tasn_utl.c
@@ -118,7 +118,6 @@
 }
 
 static ASN1_ENCODING *asn1_get_enc_ptr(ASN1_VALUE **pval, const ASN1_ITEM *it) {
-  assert(it->itype == ASN1_ITYPE_SEQUENCE);
   const ASN1_AUX *aux;
   if (!pval || !*pval) {
     return NULL;
diff --git a/src/crypto/base64/base64.c b/src/crypto/base64/base64.c
index 3d92059..349452d 100644
--- a/src/crypto/base64/base64.c
+++ b/src/crypto/base64/base64.c
@@ -265,17 +265,14 @@
   const uint8_t is_slash = constant_time_eq_8(a, '/');
   const uint8_t is_equals = constant_time_eq_8(a, '=');
 
-  uint8_t ret = 0;
-  ret |= is_upper & (a - 'A');       // [0,26)
-  ret |= is_lower & (a - 'a' + 26);  // [26,52)
-  ret |= is_digit & (a - '0' + 52);  // [52,62)
-  ret |= is_plus & 62;
-  ret |= is_slash & 63;
-  // Invalid inputs, 'A', and '=' have all been mapped to zero. Map invalid
-  // inputs to 0xff. Note '=' is padding and handled separately by the caller.
-  const uint8_t is_valid =
-      is_upper | is_lower | is_digit | is_plus | is_slash | is_equals;
-  ret |= ~is_valid;
+  uint8_t ret = 0xff;  // 0xff signals invalid.
+  ret = constant_time_select_8(is_upper, a - 'A', ret);       // [0,26)
+  ret = constant_time_select_8(is_lower, a - 'a' + 26, ret);  // [26,52)
+  ret = constant_time_select_8(is_digit, a - '0' + 52, ret);  // [52,62)
+  ret = constant_time_select_8(is_plus, 62, ret);
+  ret = constant_time_select_8(is_slash, 63, ret);
+  // Padding maps to zero, to be further handled by the caller.
+  ret = constant_time_select_8(is_equals, 0, ret);
   return ret;
 }
 
diff --git a/src/crypto/bio/bio_mem.c b/src/crypto/bio/bio_mem.c
index f40a9a7..08dd6e9 100644
--- a/src/crypto/bio/bio_mem.c
+++ b/src/crypto/bio/bio_mem.c
@@ -116,11 +116,17 @@
 }
 
 static int mem_free(BIO *bio) {
+  BUF_MEM *b;
+
+  if (bio == NULL) {
+    return 0;
+  }
+
   if (!bio->shutdown || !bio->init || bio->ptr == NULL) {
     return 1;
   }
 
-  BUF_MEM *b = (BUF_MEM *)bio->ptr;
+  b = (BUF_MEM *)bio->ptr;
   if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
     b->data = NULL;
   }
diff --git a/src/crypto/bio/connect.c b/src/crypto/bio/connect.c
index 3b65acf..b8afa61 100644
--- a/src/crypto/bio/connect.c
+++ b/src/crypto/bio/connect.c
@@ -320,7 +320,7 @@
   bio->init = 0;
   bio->num = -1;
   bio->flags = 0;
-  bio->ptr = BIO_CONNECT_new();
+  bio->ptr = (char *)BIO_CONNECT_new();
   return bio->ptr != NULL;
 }
 
@@ -340,6 +340,10 @@
 }
 
 static int conn_free(BIO *bio) {
+  if (bio == NULL) {
+    return 0;
+  }
+
   if (bio->shutdown) {
     conn_close_socket(bio);
   }
diff --git a/src/crypto/bio/fd.c b/src/crypto/bio/fd.c
index 349ee9d..d4e6918 100644
--- a/src/crypto/bio/fd.c
+++ b/src/crypto/bio/fd.c
@@ -146,6 +146,10 @@
 }
 
 static int fd_free(BIO *bio) {
+  if (bio == NULL) {
+    return 0;
+  }
+
   if (bio->shutdown) {
     if (bio->init) {
       BORINGSSL_CLOSE(bio->num);
diff --git a/src/crypto/bio/file.c b/src/crypto/bio/file.c
index 835d661..15feb9d 100644
--- a/src/crypto/bio/file.c
+++ b/src/crypto/bio/file.c
@@ -126,7 +126,13 @@
   return ret;
 }
 
+static int file_new(BIO *bio) { return 1; }
+
 static int file_free(BIO *bio) {
+  if (bio == NULL) {
+    return 0;
+  }
+
   if (!bio->shutdown) {
     return 1;
   }
@@ -273,7 +279,7 @@
     BIO_TYPE_FILE,   "FILE pointer",
     file_write,      file_read,
     NULL /* puts */, file_gets,
-    file_ctrl,       NULL /* create */,
+    file_ctrl,       file_new,
     file_free,       NULL /* callback_ctrl */,
 };
 
diff --git a/src/crypto/bio/pair.c b/src/crypto/bio/pair.c
index a1a9c9c..03f60b7 100644
--- a/src/crypto/bio/pair.c
+++ b/src/crypto/bio/pair.c
@@ -127,7 +127,12 @@
 }
 
 static int bio_free(BIO *bio) {
-  struct bio_bio_st *b = bio->ptr;
+  struct bio_bio_st *b;
+
+  if (bio == NULL) {
+    return 0;
+  }
+  b = bio->ptr;
 
   assert(b != NULL);
 
diff --git a/src/crypto/bio/socket.c b/src/crypto/bio/socket.c
index 679959e..081ce01 100644
--- a/src/crypto/bio/socket.c
+++ b/src/crypto/bio/socket.c
@@ -81,7 +81,19 @@
 }
 #endif
 
+static int sock_new(BIO *bio) {
+  bio->init = 0;
+  bio->num = 0;
+  bio->ptr = NULL;
+  bio->flags = 0;
+  return 1;
+}
+
 static int sock_free(BIO *bio) {
+  if (bio == NULL) {
+    return 0;
+  }
+
   if (bio->shutdown) {
     if (bio->init) {
       closesocket(bio->num);
@@ -93,15 +105,17 @@
 }
 
 static int sock_read(BIO *b, char *out, int outl) {
+  int ret = 0;
+
   if (out == NULL) {
     return 0;
   }
 
   bio_clear_socket_error();
 #if defined(OPENSSL_WINDOWS)
-  int ret = recv(b->num, out, outl, 0);
+  ret = recv(b->num, out, outl, 0);
 #else
-  int ret = read(b->num, out, outl);
+  ret = read(b->num, out, outl);
 #endif
   BIO_clear_retry_flags(b);
   if (ret <= 0) {
@@ -172,7 +186,7 @@
     BIO_TYPE_SOCKET, "socket",
     sock_write,      sock_read,
     NULL /* puts */, NULL /* gets, */,
-    sock_ctrl,       NULL /* create */,
+    sock_ctrl,       sock_new,
     sock_free,       NULL /* callback_ctrl */,
 };
 
diff --git a/src/crypto/bytestring/bytestring_test.cc b/src/crypto/bytestring/bytestring_test.cc
index 0877a2e..eafb0de 100644
--- a/src/crypto/bytestring/bytestring_test.cc
+++ b/src/crypto/bytestring/bytestring_test.cc
@@ -115,28 +115,6 @@
   EXPECT_FALSE(CBS_get_u24_length_prefixed(&data, &prefixed));
 }
 
-TEST(CBSTest, GetUntilFirst) {
-  static const uint8_t kData[] = {0, 1, 2, 3, 0, 1, 2, 3};
-  CBS data;
-  CBS_init(&data, kData, sizeof(kData));
-
-  CBS prefix;
-  EXPECT_FALSE(CBS_get_until_first(&data, &prefix, 4));
-  EXPECT_EQ(CBS_data(&data), kData);
-  EXPECT_EQ(CBS_len(&data), sizeof(kData));
-
-  ASSERT_TRUE(CBS_get_until_first(&data, &prefix, 0));
-  EXPECT_EQ(CBS_len(&prefix), 0u);
-  EXPECT_EQ(CBS_data(&data), kData);
-  EXPECT_EQ(CBS_len(&data), sizeof(kData));
-
-  ASSERT_TRUE(CBS_get_until_first(&data, &prefix, 2));
-  EXPECT_EQ(CBS_data(&prefix), kData);
-  EXPECT_EQ(CBS_len(&prefix), 2u);
-  EXPECT_EQ(CBS_data(&data), kData + 2);
-  EXPECT_EQ(CBS_len(&data), sizeof(kData) - 2);
-}
-
 TEST(CBSTest, GetASN1) {
   static const uint8_t kData1[] = {0x30, 2, 1, 2};
   static const uint8_t kData2[] = {0x30, 3, 1, 2};
@@ -344,11 +322,11 @@
 }
 
 TEST(CBBTest, Basic) {
-  static const uint8_t kExpected[] = {
-      0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
-      0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
-      0x03, 0x02, 0x0a, 0x09, 0x08, 0x07, 0x12, 0x11, 0x10, 0x0f,
-      0x0e, 0x0d, 0x0c, 0x0b, 0x00, 0x00, 0x00, 0x00};
+  static const uint8_t kExpected[] = {1,   2,    3,    4,    5,    6,   7,
+                                      8,   9,    0xa,  0xb,  0xc,  0xd, 0xe,
+                                      0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 3, 2,
+                                      10,  9,    8,    7,    0x12, 0x11, 0x10,
+                                      0xf, 0xe,  0xd,  0xc,  0xb};
   uint8_t *buf;
   size_t buf_len;
 
@@ -357,7 +335,6 @@
   cbb.Reset();
 
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
-  ASSERT_TRUE(CBB_add_zeros(cbb.get(), 0));
   ASSERT_TRUE(CBB_add_u8(cbb.get(), 1));
   ASSERT_TRUE(CBB_add_u16(cbb.get(), 0x203));
   ASSERT_TRUE(CBB_add_u24(cbb.get(), 0x40506));
@@ -367,7 +344,6 @@
   ASSERT_TRUE(CBB_add_u16le(cbb.get(), 0x203));
   ASSERT_TRUE(CBB_add_u32le(cbb.get(), 0x708090a));
   ASSERT_TRUE(CBB_add_u64le(cbb.get(), 0xb0c0d0e0f101112));
-  ASSERT_TRUE(CBB_add_zeros(cbb.get(), 4));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
 
   bssl::UniquePtr<uint8_t> scoper(buf);
diff --git a/src/crypto/bytestring/cbb.c b/src/crypto/bytestring/cbb.c
index 12587cd..efb89c7 100644
--- a/src/crypto/bytestring/cbb.c
+++ b/src/crypto/bytestring/cbb.c
@@ -404,15 +404,6 @@
   return 1;
 }
 
-int CBB_add_zeros(CBB *cbb, size_t len) {
-  uint8_t *out;
-  if (!CBB_add_space(cbb, &out, len)) {
-    return 0;
-  }
-  OPENSSL_memset(out, 0, len);
-  return 1;
-}
-
 int CBB_add_space(CBB *cbb, uint8_t **out_data, size_t len) {
   if (!CBB_flush(cbb) ||
       !cbb_buffer_add(cbb->base, out_data, len)) {
diff --git a/src/crypto/bytestring/cbs.c b/src/crypto/bytestring/cbs.c
index 803c97a..5590ec8 100644
--- a/src/crypto/bytestring/cbs.c
+++ b/src/crypto/bytestring/cbs.c
@@ -216,14 +216,6 @@
   return cbs_get_length_prefixed(cbs, out, 3);
 }
 
-int CBS_get_until_first(CBS *cbs, CBS *out, uint8_t c) {
-  const uint8_t *split = OPENSSL_memchr(CBS_data(cbs), c, CBS_len(cbs));
-  if (split == NULL) {
-    return 0;
-  }
-  return CBS_get_bytes(cbs, out, split - CBS_data(cbs));
-}
-
 // parse_base128_integer reads a big-endian base-128 integer from |cbs| and sets
 // |*out| to the result. This is the encoding used in DER for both high tag
 // number form and OID components.
diff --git a/src/crypto/chacha/asm/chacha-armv4.pl b/src/crypto/chacha/asm/chacha-armv4.pl
index 5c78a9f..5455b83 100755
--- a/src/crypto/chacha/asm/chacha-armv4.pl
+++ b/src/crypto/chacha/asm/chacha-armv4.pl
@@ -1163,4 +1163,4 @@
 
 	print $_,"\n";
 }
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/chacha/asm/chacha-armv8.pl b/src/crypto/chacha/asm/chacha-armv8.pl
index 608db66..b2d3d86 100755
--- a/src/crypto/chacha/asm/chacha-armv8.pl
+++ b/src/crypto/chacha/asm/chacha-armv8.pl
@@ -1141,4 +1141,4 @@
 
 	print $_,"\n";
 }
-close STDOUT or die "error closing STDOUT: $!";	# flush
+close STDOUT or die "error closing STDOUT";	# flush
diff --git a/src/crypto/chacha/asm/chacha-x86.pl b/src/crypto/chacha/asm/chacha-x86.pl
index 482d53f..ec1cf80 100755
--- a/src/crypto/chacha/asm/chacha-x86.pl
+++ b/src/crypto/chacha/asm/chacha-x86.pl
@@ -769,4 +769,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/chacha/asm/chacha-x86_64.pl b/src/crypto/chacha/asm/chacha-x86_64.pl
index 8f3f4ce..2ee22b6 100755
--- a/src/crypto/chacha/asm/chacha-x86_64.pl
+++ b/src/crypto/chacha/asm/chacha-x86_64.pl
@@ -2782,4 +2782,4 @@
 	print $_,"\n";
 }
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/cipher_extra/asm/aes128gcmsiv-x86_64.pl b/src/crypto/cipher_extra/asm/aes128gcmsiv-x86_64.pl
index aad31ab..54f0b5c 100644
--- a/src/crypto/cipher_extra/asm/aes128gcmsiv-x86_64.pl
+++ b/src/crypto/cipher_extra/asm/aes128gcmsiv-x86_64.pl
@@ -2253,4 +2253,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl b/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
index b2067c7..aa42c67 100644
--- a/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
+++ b/src/crypto/cipher_extra/asm/chacha20_poly1305_x86_64.pl
@@ -2559,4 +2559,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/cipher_extra/test/cipher_tests.txt b/src/crypto/cipher_extra/test/cipher_tests.txt
index 782b08e..bf7325e 100644
--- a/src/crypto/cipher_extra/test/cipher_tests.txt
+++ b/src/crypto/cipher_extra/test/cipher_tests.txt
@@ -271,7 +271,7 @@
 Ciphertext =
 
 
-# AES Counter test vectors from RFC 3686
+# AES Counter test vectors from RFC3686
 Cipher = AES-128-CTR
 Key = AE6852F8121067CC4BF7A5765577F39E
 IV = 00000030000000000000000000000001
diff --git a/src/crypto/crypto_test.cc b/src/crypto/crypto_test.cc
index 7f15a23..03b909d 100644
--- a/src/crypto/crypto_test.cc
+++ b/src/crypto/crypto_test.cc
@@ -18,10 +18,8 @@
 #include <string>
 
 #include <openssl/base.h>
-#include <openssl/aead.h>
 #include <openssl/crypto.h>
 #include <openssl/cipher.h>
-#include <openssl/mem.h>
 
 #include <gtest/gtest.h>
 
@@ -37,104 +35,40 @@
             std::string(OPENSSL_VERSION_TEXT).substr(0, strlen(expected)));
 }
 
-TEST(CryptoTest, Strndup) {
-  bssl::UniquePtr<char> str(OPENSSL_strndup(nullptr, 0));
-  EXPECT_TRUE(str);
-  EXPECT_STREQ("", str.get());
-}
-
 #if defined(BORINGSSL_FIPS_COUNTERS)
-using CounterArray = size_t[fips_counter_max + 1];
-
-static void read_all_counters(CounterArray counters) {
-  for (fips_counter_t counter = static_cast<fips_counter_t>(0);
-       counter <= fips_counter_max;
-       counter = static_cast<fips_counter_t>(counter + 1)) {
-    counters[counter] = FIPS_read_counter(counter);
-  }
-}
-
-static void expect_counter_delta_is_zero_except_for_a_one_at(
-    CounterArray before, CounterArray after, fips_counter_t position) {
-  for (fips_counter_t counter = static_cast<fips_counter_t>(0);
-       counter <= fips_counter_max;
-       counter = static_cast<fips_counter_t>(counter + 1)) {
-    const size_t expected_delta = counter == position ? 1 : 0;
-    EXPECT_EQ(after[counter], before[counter] + expected_delta) << counter;
-  }
-}
-
 TEST(CryptoTest, FIPSCountersEVP) {
   constexpr struct {
     const EVP_CIPHER *(*cipher)();
     fips_counter_t counter;
   } kTests[] = {
-      {
-          EVP_aes_128_gcm,
-          fips_counter_evp_aes_128_gcm,
-      },
-      {
-          EVP_aes_256_gcm,
-          fips_counter_evp_aes_256_gcm,
-      },
-      {
-          EVP_aes_128_ctr,
-          fips_counter_evp_aes_128_ctr,
-      },
-      {
-          EVP_aes_256_ctr,
-          fips_counter_evp_aes_256_ctr,
-      },
+    {
+        EVP_aes_128_gcm,
+        fips_counter_evp_aes_128_gcm,
+    },
+    {
+        EVP_aes_256_gcm,
+        fips_counter_evp_aes_256_gcm,
+    },
+    {
+        EVP_aes_128_ctr,
+        fips_counter_evp_aes_128_ctr,
+    },
+    {
+        EVP_aes_256_ctr,
+        fips_counter_evp_aes_256_ctr,
+    },
   };
 
   uint8_t key[EVP_MAX_KEY_LENGTH] = {0};
   uint8_t iv[EVP_MAX_IV_LENGTH] = {1};
-  CounterArray before, after;
-  for (const auto &test : kTests) {
-    read_all_counters(before);
+
+  for (const auto& test : kTests) {
+    const size_t before = FIPS_read_counter(test.counter);
+
     bssl::ScopedEVP_CIPHER_CTX ctx;
     ASSERT_TRUE(EVP_EncryptInit_ex(ctx.get(), test.cipher(), /*engine=*/nullptr,
                                    key, iv));
-    read_all_counters(after);
-
-    expect_counter_delta_is_zero_except_for_a_one_at(before, after,
-                                                     test.counter);
+    ASSERT_GT(FIPS_read_counter(test.counter), before);
   }
 }
-
-TEST(CryptoTest, FIPSCountersEVP_AEAD) {
-  constexpr struct {
-    const EVP_AEAD *(*aead)();
-    unsigned key_len;
-    fips_counter_t counter;
-  } kTests[] = {
-      {
-          EVP_aead_aes_128_gcm,
-          16,
-          fips_counter_evp_aes_128_gcm,
-      },
-      {
-          EVP_aead_aes_256_gcm,
-          32,
-          fips_counter_evp_aes_256_gcm,
-      },
-  };
-
-  uint8_t key[EVP_AEAD_MAX_KEY_LENGTH] = {0};
-  CounterArray before, after;
-  for (const auto &test : kTests) {
-    ASSERT_LE(test.key_len, sizeof(key));
-
-    read_all_counters(before);
-    bssl::ScopedEVP_AEAD_CTX ctx;
-    ASSERT_TRUE(EVP_AEAD_CTX_init(ctx.get(), test.aead(), key, test.key_len,
-                                  EVP_AEAD_DEFAULT_TAG_LENGTH,
-                                  /*engine=*/nullptr));
-    read_all_counters(after);
-
-    expect_counter_delta_is_zero_except_for_a_one_at(before, after,
-                                                     test.counter);
-  }
-}
-
 #endif  // BORINGSSL_FIPS_COUNTERS
diff --git a/src/crypto/digest_extra/digest_extra.c b/src/crypto/digest_extra/digest_extra.c
index 8cbb28e..a93601c 100644
--- a/src/crypto/digest_extra/digest_extra.c
+++ b/src/crypto/digest_extra/digest_extra.c
@@ -83,7 +83,6 @@
     {NID_sha256, EVP_sha256, SN_sha256, LN_sha256},
     {NID_sha384, EVP_sha384, SN_sha384, LN_sha384},
     {NID_sha512, EVP_sha512, SN_sha512, LN_sha512},
-    {NID_sha512_256, EVP_sha512_256, SN_sha512_256, LN_sha512_256},
     {NID_md5_sha1, EVP_md5_sha1, SN_md5_sha1, LN_md5_sha1},
     // As a remnant of signing |EVP_MD|s, OpenSSL returned the corresponding
     // hash function when given a signature OID. To avoid unintended lax parsing
diff --git a/src/crypto/err/ssl.errordata b/src/crypto/err/ssl.errordata
index 6879134..2f85410 100644
--- a/src/crypto/err/ssl.errordata
+++ b/src/crypto/err/ssl.errordata
@@ -80,7 +80,6 @@
 SSL,156,HTTP_REQUEST
 SSL,157,INAPPROPRIATE_FALLBACK
 SSL,303,INCONSISTENT_CLIENT_HELLO
-SSL,321,INCONSISTENT_ECH_NEGOTIATION
 SSL,259,INVALID_ALPN_PROTOCOL
 SSL,315,INVALID_ALPN_PROTOCOL_LIST
 SSL,314,INVALID_CLIENT_HELLO_INNER
@@ -133,7 +132,6 @@
 SSL,187,OLD_SESSION_CIPHER_NOT_RETURNED
 SSL,268,OLD_SESSION_PRF_HASH_MISMATCH
 SSL,188,OLD_SESSION_VERSION_NOT_RETURNED
-SSL,320,OUTER_EXTENSION_NOT_FOUND
 SSL,189,OUTPUT_ALIASES_INPUT
 SSL,190,PARSE_TLSEXT
 SSL,191,PATH_TOO_LONG
diff --git a/src/crypto/err/x509v3.errordata b/src/crypto/err/x509v3.errordata
index 492259c..e53b780 100644
--- a/src/crypto/err/x509v3.errordata
+++ b/src/crypto/err/x509v3.errordata
@@ -34,7 +34,6 @@
 X509V3,133,INVALID_PURPOSE
 X509V3,134,INVALID_SECTION
 X509V3,135,INVALID_SYNTAX
-X509V3,163,INVALID_VALUE
 X509V3,136,ISSUER_DECODE_ERROR
 X509V3,137,MISSING_VALUE
 X509V3,138,NEED_ORGANIZATION_AND_NUMBERS
diff --git a/src/crypto/fipsmodule/CMakeLists.txt b/src/crypto/fipsmodule/CMakeLists.txt
index 73f8a02..7b9e1b1 100644
--- a/src/crypto/fipsmodule/CMakeLists.txt
+++ b/src/crypto/fipsmodule/CMakeLists.txt
@@ -1,6 +1,6 @@
 include_directories(../../include)
 
-if(ARCH STREQUAL "x86_64")
+if(${ARCH} STREQUAL "x86_64")
   set(
     BCM_ASM_SOURCES
 
@@ -22,7 +22,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "x86")
+if(${ARCH} STREQUAL "x86")
   set(
     BCM_ASM_SOURCES
 
@@ -40,7 +40,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "arm")
+if(${ARCH} STREQUAL "arm")
   set(
     BCM_ASM_SOURCES
 
@@ -56,7 +56,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "aarch64")
+if(${ARCH} STREQUAL "aarch64")
   set(
     BCM_ASM_SOURCES
 
@@ -71,7 +71,7 @@
   )
 endif()
 
-if(ARCH STREQUAL "ppc64le")
+if(${ARCH} STREQUAL "ppc64le")
   set(
     BCM_ASM_SOURCES
 
@@ -160,7 +160,7 @@
     bcm.c
   )
 
-  if(ARCH STREQUAL "aarch64")
+  if(${ARCH} STREQUAL "aarch64")
     # Perlasm output on Aarch64 needs to pass through the C preprocessor before
     # it can be parsed by delocate.
     foreach(asm ${BCM_ASM_SOURCES})
@@ -202,7 +202,7 @@
   # If building with OPENSSL_NO_ASM then ARCH will be "generic", but we still
   # need to use SHA-256. Since this only matters for FIPS, we only need to
   # worry about the Linux spelling of AArch64.
-  if (ARCH STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+  if (ARCH STREQUAL "aarch64" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
     set(MAYBE_INJECT_HASH_SHA256_FLAG "-sha256")
   endif()
 
diff --git a/src/crypto/fipsmodule/aes/asm/aesni-x86.pl b/src/crypto/fipsmodule/aes/asm/aesni-x86.pl
index 7d97adc..b999e5a 100644
--- a/src/crypto/fipsmodule/aes/asm/aesni-x86.pl
+++ b/src/crypto/fipsmodule/aes/asm/aesni-x86.pl
@@ -2551,4 +2551,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/aesni-x86_64.pl b/src/crypto/fipsmodule/aes/asm/aesni-x86_64.pl
index 2abc8d0..f4d2ea5 100644
--- a/src/crypto/fipsmodule/aes/asm/aesni-x86_64.pl
+++ b/src/crypto/fipsmodule/aes/asm/aesni-x86_64.pl
@@ -5102,4 +5102,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/aesp8-ppc.pl b/src/crypto/fipsmodule/aes/asm/aesp8-ppc.pl
index 983cbbe..3b1fbee 100644
--- a/src/crypto/fipsmodule/aes/asm/aesp8-ppc.pl
+++ b/src/crypto/fipsmodule/aes/asm/aesp8-ppc.pl
@@ -3804,4 +3804,4 @@
         print $_,"\n";
 }
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/aesv8-armx.pl b/src/crypto/fipsmodule/aes/asm/aesv8-armx.pl
index 82022c7..3bfdd8d 100644
--- a/src/crypto/fipsmodule/aes/asm/aesv8-armx.pl
+++ b/src/crypto/fipsmodule/aes/asm/aesv8-armx.pl
@@ -1044,4 +1044,4 @@
     }
 }
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/bsaes-armv7.pl b/src/crypto/fipsmodule/aes/asm/bsaes-armv7.pl
index c537730..f6db486 100644
--- a/src/crypto/fipsmodule/aes/asm/bsaes-armv7.pl
+++ b/src/crypto/fipsmodule/aes/asm/bsaes-armv7.pl
@@ -2433,4 +2433,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/vpaes-armv7.pl b/src/crypto/fipsmodule/aes/asm/vpaes-armv7.pl
index b4f2eb3..bb77d10 100644
--- a/src/crypto/fipsmodule/aes/asm/vpaes-armv7.pl
+++ b/src/crypto/fipsmodule/aes/asm/vpaes-armv7.pl
@@ -1373,4 +1373,4 @@
 	print $_,"\n";
 }
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT;
diff --git a/src/crypto/fipsmodule/aes/asm/vpaes-armv8.pl b/src/crypto/fipsmodule/aes/asm/vpaes-armv8.pl
index 8b01fed..a1d919d 100755
--- a/src/crypto/fipsmodule/aes/asm/vpaes-armv8.pl
+++ b/src/crypto/fipsmodule/aes/asm/vpaes-armv8.pl
@@ -1382,4 +1382,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/vpaes-x86.pl b/src/crypto/fipsmodule/aes/asm/vpaes-x86.pl
index 04c407d..14a3105 100644
--- a/src/crypto/fipsmodule/aes/asm/vpaes-x86.pl
+++ b/src/crypto/fipsmodule/aes/asm/vpaes-x86.pl
@@ -920,4 +920,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/aes/asm/vpaes-x86_64.pl b/src/crypto/fipsmodule/aes/asm/vpaes-x86_64.pl
index f6f67ea..e761eb2 100644
--- a/src/crypto/fipsmodule/aes/asm/vpaes-x86_64.pl
+++ b/src/crypto/fipsmodule/aes/asm/vpaes-x86_64.pl
@@ -1546,4 +1546,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/armv4-mont.pl b/src/crypto/fipsmodule/bn/asm/armv4-mont.pl
index 207b8e4..5ee474f 100644
--- a/src/crypto/fipsmodule/bn/asm/armv4-mont.pl
+++ b/src/crypto/fipsmodule/bn/asm/armv4-mont.pl
@@ -761,4 +761,4 @@
 	print $_,"\n";
 }
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/armv8-mont.pl b/src/crypto/fipsmodule/bn/asm/armv8-mont.pl
index aa65ffd..77b437d 100644
--- a/src/crypto/fipsmodule/bn/asm/armv8-mont.pl
+++ b/src/crypto/fipsmodule/bn/asm/armv8-mont.pl
@@ -1520,4 +1520,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/bn-586.pl b/src/crypto/fipsmodule/bn/asm/bn-586.pl
index c23d6a9..5c52e05 100644
--- a/src/crypto/fipsmodule/bn/asm/bn-586.pl
+++ b/src/crypto/fipsmodule/bn/asm/bn-586.pl
@@ -30,7 +30,7 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
 
 sub bn_mul_add_words
 	{
diff --git a/src/crypto/fipsmodule/bn/asm/co-586.pl b/src/crypto/fipsmodule/bn/asm/co-586.pl
index 458db33..abe328a 100644
--- a/src/crypto/fipsmodule/bn/asm/co-586.pl
+++ b/src/crypto/fipsmodule/bn/asm/co-586.pl
@@ -22,7 +22,7 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
 
 sub mul_add_c
 	{
diff --git a/src/crypto/fipsmodule/bn/asm/rsaz-avx2.pl b/src/crypto/fipsmodule/bn/asm/rsaz-avx2.pl
index 65b0062..a0da239 100755
--- a/src/crypto/fipsmodule/bn/asm/rsaz-avx2.pl
+++ b/src/crypto/fipsmodule/bn/asm/rsaz-avx2.pl
@@ -1940,4 +1940,4 @@
 ___
 }}}
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/x86-mont.pl b/src/crypto/fipsmodule/bn/asm/x86-mont.pl
index 58b28ad..1f61ae5 100755
--- a/src/crypto/fipsmodule/bn/asm/x86-mont.pl
+++ b/src/crypto/fipsmodule/bn/asm/x86-mont.pl
@@ -628,4 +628,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/x86_64-mont.pl b/src/crypto/fipsmodule/bn/asm/x86_64-mont.pl
index 8b34ae3..0a9e4d1 100755
--- a/src/crypto/fipsmodule/bn/asm/x86_64-mont.pl
+++ b/src/crypto/fipsmodule/bn/asm/x86_64-mont.pl
@@ -1578,4 +1578,4 @@
 }
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/asm/x86_64-mont5.pl b/src/crypto/fipsmodule/bn/asm/x86_64-mont5.pl
index 54335cc..b2ff114 100755
--- a/src/crypto/fipsmodule/bn/asm/x86_64-mont5.pl
+++ b/src/crypto/fipsmodule/bn/asm/x86_64-mont5.pl
@@ -3930,4 +3930,4 @@
 $code =~ s/\`([^\`]*)\`/eval($1)/gem;
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/bn/bn_test.cc b/src/crypto/fipsmodule/bn/bn_test.cc
index 72ec8c2..1b42d9c 100644
--- a/src/crypto/fipsmodule/bn/bn_test.cc
+++ b/src/crypto/fipsmodule/bn/bn_test.cc
@@ -613,17 +613,9 @@
     }
   }
 
-  ASSERT_TRUE(bn_div_consttime(ret.get(), ret2.get(), a.get(), b.get(),
-                               /*divisor_min_bits=*/0, ctx));
+  ASSERT_TRUE(bn_div_consttime(ret.get(), ret2.get(), a.get(), b.get(), ctx));
   EXPECT_BIGNUMS_EQUAL("A / B (constant-time)", quotient.get(), ret.get());
   EXPECT_BIGNUMS_EQUAL("A % B (constant-time)", remainder.get(), ret2.get());
-
-  ASSERT_TRUE(bn_div_consttime(ret.get(), ret2.get(), a.get(), b.get(),
-                               /*divisor_min_bits=*/BN_num_bits(b.get()), ctx));
-  EXPECT_BIGNUMS_EQUAL("A / B (constant-time, public width)", quotient.get(),
-                       ret.get());
-  EXPECT_BIGNUMS_EQUAL("A % B (constant-time, public width)", remainder.get(),
-                       ret2.get());
 }
 
 static void TestModMul(BIGNUMFileTest *t, BN_CTX *ctx) {
diff --git a/src/crypto/fipsmodule/bn/div.c b/src/crypto/fipsmodule/bn/div.c
index 02b9931..6ee6dbd 100644
--- a/src/crypto/fipsmodule/bn/div.c
+++ b/src/crypto/fipsmodule/bn/div.c
@@ -456,7 +456,7 @@
 
 int bn_div_consttime(BIGNUM *quotient, BIGNUM *remainder,
                      const BIGNUM *numerator, const BIGNUM *divisor,
-                     unsigned divisor_min_bits, BN_CTX *ctx) {
+                     BN_CTX *ctx) {
   if (BN_is_negative(numerator) || BN_is_negative(divisor)) {
     OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
     return 0;
@@ -496,26 +496,8 @@
   r->neg = 0;
 
   // Incorporate |numerator| into |r|, one bit at a time, reducing after each
-  // step. We maintain the invariant that |0 <= r < divisor| and
-  // |q * divisor + r = n| where |n| is the portion of |numerator| incorporated
-  // so far.
-  //
-  // First, we short-circuit the loop: if we know |divisor| has at least
-  // |divisor_min_bits| bits, the top |divisor_min_bits - 1| can be incorporated
-  // without reductions. This significantly speeds up |RSA_check_key|. For
-  // simplicity, we round down to a whole number of words.
-  assert(divisor_min_bits <= BN_num_bits(divisor));
-  int initial_words = 0;
-  if (divisor_min_bits > 0) {
-    initial_words = (divisor_min_bits - 1) / BN_BITS2;
-    if (initial_words > numerator->width) {
-      initial_words = numerator->width;
-    }
-    OPENSSL_memcpy(r->d, numerator->d + numerator->width - initial_words,
-                   initial_words * sizeof(BN_ULONG));
-  }
-
-  for (int i = numerator->width - initial_words - 1; i >= 0; i--) {
+  // step. At the start of each loop iteration, |r| < |divisor|
+  for (int i = numerator->width - 1; i >= 0; i--) {
     for (int bit = BN_BITS2 - 1; bit >= 0; bit--) {
       // Incorporate the next bit of the numerator, by computing
       // r = 2*r or 2*r + 1. Note the result fits in one more word. We store the
diff --git a/src/crypto/fipsmodule/bn/gcd_extra.c b/src/crypto/fipsmodule/bn/gcd_extra.c
index 53ab170..30540e3 100644
--- a/src/crypto/fipsmodule/bn/gcd_extra.c
+++ b/src/crypto/fipsmodule/bn/gcd_extra.c
@@ -157,11 +157,10 @@
   BN_CTX_start(ctx);
   unsigned shift;
   BIGNUM *gcd = BN_CTX_get(ctx);
-  int ret = gcd != NULL &&  //
+  int ret = gcd != NULL &&
             bn_mul_consttime(r, a, b, ctx) &&
             bn_gcd_consttime(gcd, &shift, a, b, ctx) &&
-            // |gcd| has a secret bit width.
-            bn_div_consttime(r, NULL, r, gcd, /*divisor_min_bits=*/0, ctx) &&
+            bn_div_consttime(r, NULL, r, gcd, ctx) &&
             bn_rshift_secret_shift(r, r, shift, ctx);
   BN_CTX_end(ctx);
   return ret;
diff --git a/src/crypto/fipsmodule/bn/internal.h b/src/crypto/fipsmodule/bn/internal.h
index cab9a81..3d368db 100644
--- a/src/crypto/fipsmodule/bn/internal.h
+++ b/src/crypto/fipsmodule/bn/internal.h
@@ -552,15 +552,12 @@
 // bn_div_consttime behaves like |BN_div|, but it rejects negative inputs and
 // treats both inputs, including their magnitudes, as secret. It is, as a
 // result, much slower than |BN_div| and should only be used for rare operations
-// where Montgomery reduction is not available. |divisor_min_bits| is a
-// public lower bound for |BN_num_bits(divisor)|. When |divisor|'s bit width is
-// public, this can speed up the operation.
+// where Montgomery reduction is not available.
 //
 // Note that |quotient->width| will be set pessimally to |numerator->width|.
 OPENSSL_EXPORT int bn_div_consttime(BIGNUM *quotient, BIGNUM *remainder,
                                     const BIGNUM *numerator,
-                                    const BIGNUM *divisor,
-                                    unsigned divisor_min_bits, BN_CTX *ctx);
+                                    const BIGNUM *divisor, BN_CTX *ctx);
 
 // bn_is_relatively_prime checks whether GCD(|x|, |y|) is one. On success, it
 // returns one and sets |*out_relatively_prime| to one if the GCD was one and
diff --git a/src/crypto/fipsmodule/cipher/e_aes.c b/src/crypto/fipsmodule/cipher/e_aes.c
index 76f4066..f77133f 100644
--- a/src/crypto/fipsmodule/cipher/e_aes.c
+++ b/src/crypto/fipsmodule/cipher/e_aes.c
@@ -911,16 +911,6 @@
                                   size_t key_len, size_t tag_len) {
   const size_t key_bits = key_len * 8;
 
-  switch (key_bits) {
-    case 128:
-      boringssl_fips_inc_counter(fips_counter_evp_aes_128_gcm);
-      break;
-
-    case 256:
-      boringssl_fips_inc_counter(fips_counter_evp_aes_256_gcm);
-      break;
-  }
-
   if (key_bits != 128 && key_bits != 192 && key_bits != 256) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_KEY_LENGTH);
     return 0;  // EVP_AEAD_CTX_init should catch this.
diff --git a/src/crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl b/src/crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl
index c75d784..994cb82 100755
--- a/src/crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl
+++ b/src/crypto/fipsmodule/ec/asm/p256-x86_64-asm.pl
@@ -4153,4 +4153,4 @@
 
 $code =~ s/\`([^\`]*)\`/eval $1/gem;
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/ec/asm/p256_beeu-x86_64-asm.pl b/src/crypto/fipsmodule/ec/asm/p256_beeu-x86_64-asm.pl
index b9ec96d..c05abba 100644
--- a/src/crypto/fipsmodule/ec/asm/p256_beeu-x86_64-asm.pl
+++ b/src/crypto/fipsmodule/ec/asm/p256_beeu-x86_64-asm.pl
@@ -400,4 +400,4 @@
 ___
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/md4/md4.c b/src/crypto/fipsmodule/md4/md4.c
index a505d05..8779402 100644
--- a/src/crypto/fipsmodule/md4/md4.c
+++ b/src/crypto/fipsmodule/md4/md4.c
@@ -72,7 +72,7 @@
   return out;
 }
 
-// Implemented from RFC 1186 The MD4 Message-Digest Algorithm.
+// Implemented from RFC1186 The MD4 Message-Digest Algorithm.
 
 int MD4_Init(MD4_CTX *md4) {
   OPENSSL_memset(md4, 0, sizeof(MD4_CTX));
diff --git a/src/crypto/fipsmodule/md5/asm/md5-586.pl b/src/crypto/fipsmodule/md5/asm/md5-586.pl
index ec34aad..20c226b 100644
--- a/src/crypto/fipsmodule/md5/asm/md5-586.pl
+++ b/src/crypto/fipsmodule/md5/asm/md5-586.pl
@@ -36,7 +36,7 @@
 &md5_block("md5_block_asm_data_order");
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
 
 sub Np
 	{
diff --git a/src/crypto/fipsmodule/md5/asm/md5-x86_64.pl b/src/crypto/fipsmodule/md5/asm/md5-x86_64.pl
index 4c96bba..509bcde 100644
--- a/src/crypto/fipsmodule/md5/asm/md5-x86_64.pl
+++ b/src/crypto/fipsmodule/md5/asm/md5-x86_64.pl
@@ -380,4 +380,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl b/src/crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl
index 793f34c..48a5560 100644
--- a/src/crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl
+++ b/src/crypto/fipsmodule/modes/asm/aesni-gcm-x86_64.pl
@@ -1141,4 +1141,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-armv4.pl b/src/crypto/fipsmodule/modes/asm/ghash-armv4.pl
index e84063b..d570990 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-armv4.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-armv4.pl
@@ -297,4 +297,4 @@
 
 	print $_,"\n";
 }
-close STDOUT or die "error closing STDOUT: $!"; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl b/src/crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl
index c6a7af6..b1725a8 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-neon-armv8.pl
@@ -291,4 +291,4 @@
 
 	print $_,"\n";
 }
-close STDOUT or die "error closing STDOUT: $!"; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86.pl b/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86.pl
index 6382876..45e1ee1 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86.pl
@@ -285,4 +285,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86_64.pl b/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86_64.pl
index f736473..e0e5010 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86_64.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-ssse3-x86_64.pl
@@ -410,4 +410,4 @@
 }
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-x86.pl b/src/crypto/fipsmodule/modes/asm/ghash-x86.pl
index f893b50..9486c2e 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-x86.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-x86.pl
@@ -660,7 +660,7 @@
 &asciz("GHASH for x86, CRYPTOGAMS by <appro\@openssl.org>");
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
 
 # A question was risen about choice of vanilla MMX. Or rather why wasn't
 # SSE2 chosen instead? In addition to the fact that MMX runs on legacy
diff --git a/src/crypto/fipsmodule/modes/asm/ghash-x86_64.pl b/src/crypto/fipsmodule/modes/asm/ghash-x86_64.pl
index 1aeb7b7..16f7f00 100644
--- a/src/crypto/fipsmodule/modes/asm/ghash-x86_64.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghash-x86_64.pl
@@ -1336,4 +1336,4 @@
 
 print $code;
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/modes/asm/ghashp8-ppc.pl b/src/crypto/fipsmodule/modes/asm/ghashp8-ppc.pl
index e9ca11a..827ddb2 100644
--- a/src/crypto/fipsmodule/modes/asm/ghashp8-ppc.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghashp8-ppc.pl
@@ -667,4 +667,4 @@
 	print $_,"\n";
 }
 
-close STDOUT or die "error closing STDOUT: $!"; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/modes/asm/ghashv8-armx.pl b/src/crypto/fipsmodule/modes/asm/ghashv8-armx.pl
index 24eb773..0fb6414 100644
--- a/src/crypto/fipsmodule/modes/asm/ghashv8-armx.pl
+++ b/src/crypto/fipsmodule/modes/asm/ghashv8-armx.pl
@@ -1,5 +1,5 @@
 #! /usr/bin/env perl
-# Copyright 2014-2020 The OpenSSL Project Authors. All Rights Reserved.
+# Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
 #
 # Licensed under the OpenSSL license (the "License").  You may not use
 # this file except in compliance with the License.  You can obtain a copy
@@ -17,31 +17,23 @@
 # GHASH for ARMv8 Crypto Extension, 64-bit polynomial multiplication.
 #
 # June 2014
-#
-# Initial version was developed in tight cooperation with Ard
-# Biesheuvel of Linaro from bits-n-pieces from other assembly modules.
-# Just like aesv8-armx.pl this module supports both AArch32 and
-# AArch64 execution modes.
+# Initial version was developed in tight cooperation with Ard Biesheuvel
+# of Linaro from bits-n-pieces from other assembly modules. Just like
+# aesv8-armx.pl this module supports both AArch32 and AArch64 execution modes.
 #
 # July 2014
-#
 # Implement 2x aggregated reduction [see ghash-x86.pl for background
 # information].
 #
-# November 2017
-#
-# AArch64 register bank to "accommodate" 4x aggregated reduction and
-# improve performance by 20-70% depending on processor.
-#
 # Current performance in cycles per processed byte:
 #
-#		64-bit PMULL	32-bit PMULL	32-bit NEON(*)
-# Apple A7	0.58		0.92		5.62
-# Cortex-A53	0.85		1.01		8.39
-# Cortex-A57	0.73		1.17		7.61
-# Denver	0.51		0.65		6.02
-# Mongoose	0.65		1.10		8.06
-# Kryo		0.76		1.16		8.00
+#		PMULL[2]	32-bit NEON(*)
+# Apple A7	0.92		5.62
+# Cortex-A53	1.01		8.39
+# Cortex-A57	1.17		7.61
+# Denver	0.71		6.02
+# Mongoose	1.10		8.06
+# Kryo		1.16		8.00
 #
 # (*)	presented for reference/comparison purposes;
 
@@ -70,7 +62,6 @@
 $code=<<___;
 #include <openssl/arm_arch.h>
 
-#if __ARM_MAX_ARCH__>=7
 .text
 ___
 $code.=".arch	armv8-a+crypto\n"	if ($flavour =~ /64/);
@@ -138,56 +129,8 @@
 	vext.8		$t1,$H2,$H2,#8		@ Karatsuba pre-processing
 	veor		$t1,$t1,$H2
 	vext.8		$Hhl,$t0,$t1,#8		@ pack Karatsuba pre-processed
-	vst1.64		{$Hhl-$H2},[x0],#32	@ store Htable[1..2]
-___
-if ($flavour =~ /64/) {
-my ($t3,$Yl,$Ym,$Yh) = map("q$_",(4..7));
+	vst1.64		{$Hhl-$H2},[x0]		@ store Htable[1..2]
 
-$code.=<<___;
-	@ calculate H^3 and H^4
-	vpmull.p64	$Xl,$H, $H2
-	 vpmull.p64	$Yl,$H2,$H2
-	vpmull2.p64	$Xh,$H, $H2
-	 vpmull2.p64	$Yh,$H2,$H2
-	vpmull.p64	$Xm,$t0,$t1
-	 vpmull.p64	$Ym,$t1,$t1
-
-	vext.8		$t0,$Xl,$Xh,#8		@ Karatsuba post-processing
-	 vext.8		$t1,$Yl,$Yh,#8
-	veor		$t2,$Xl,$Xh
-	veor		$Xm,$Xm,$t0
-	 veor		$t3,$Yl,$Yh
-	 veor		$Ym,$Ym,$t1
-	veor		$Xm,$Xm,$t2
-	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase
-	 veor		$Ym,$Ym,$t3
-	 vpmull.p64	$t3,$Yl,$xC2
-
-	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
-	 vmov		$Yh#lo,$Ym#hi
-	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
-	 vmov		$Ym#hi,$Yl#lo
-	veor		$Xl,$Xm,$t2
-	 veor		$Yl,$Ym,$t3
-
-	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase
-	 vext.8		$t3,$Yl,$Yl,#8
-	vpmull.p64	$Xl,$Xl,$xC2
-	 vpmull.p64	$Yl,$Yl,$xC2
-	veor		$t2,$t2,$Xh
-	 veor		$t3,$t3,$Yh
-	veor		$H, $Xl,$t2		@ H^3
-	 veor		$H2,$Yl,$t3		@ H^4
-
-	vext.8		$t0,$H, $H,#8		@ Karatsuba pre-processing
-	 vext.8		$t1,$H2,$H2,#8
-	veor		$t0,$t0,$H
-	 veor		$t1,$t1,$H2
-	vext.8		$Hhl,$t0,$t1,#8		@ pack Karatsuba pre-processed
-	vst1.64		{$H-$H2},[x0]		@ store Htable[3..5]
-___
-}
-$code.=<<___;
 	ret
 .size	gcm_init_v8,.-gcm_init_v8
 ___
@@ -258,10 +201,6 @@
 gcm_ghash_v8:
 	AARCH64_VALID_CALL_TARGET
 ___
-$code.=<<___	if ($flavour =~ /64/);
-	cmp		$len,#64
-	b.hs		.Lgcm_ghash_v8_4x
-___
 $code.=<<___		if ($flavour !~ /64/);
 	vstmdb		sp!,{d8-d15}		@ 32-bit ABI says so
 ___
@@ -409,301 +348,10 @@
 	ret
 .size	gcm_ghash_v8,.-gcm_ghash_v8
 ___
-
-if ($flavour =~ /64/) {				# 4x subroutine
-my ($I0,$j1,$j2,$j3,
-    $I1,$I2,$I3,$H3,$H34,$H4,$Yl,$Ym,$Yh) = map("q$_",(4..7,15..23));
-
-$code.=<<___;
-.type	gcm_ghash_v8_4x,%function
-.align	4
-gcm_ghash_v8_4x:
-.Lgcm_ghash_v8_4x:
-	vld1.64		{$Xl},[$Xi]		@ load [rotated] Xi
-	vld1.64		{$H-$H2},[$Htbl],#48	@ load twisted H, ..., H^2
-	vmov.i8		$xC2,#0xe1
-	vld1.64		{$H3-$H4},[$Htbl]	@ load twisted H^3, ..., H^4
-	vshl.u64	$xC2,$xC2,#57		@ compose 0xc2.0 constant
-
-	vld1.64		{$I0-$j3},[$inp],#64
-#ifndef __ARMEB__
-	vrev64.8	$Xl,$Xl
-	vrev64.8	$j1,$j1
-	vrev64.8	$j2,$j2
-	vrev64.8	$j3,$j3
-	vrev64.8	$I0,$I0
-#endif
-	vext.8		$I3,$j3,$j3,#8
-	vext.8		$I2,$j2,$j2,#8
-	vext.8		$I1,$j1,$j1,#8
-
-	vpmull.p64	$Yl,$H,$I3		@ H·Ii+3
-	veor		$j3,$j3,$I3
-	vpmull2.p64	$Yh,$H,$I3
-	vpmull.p64	$Ym,$Hhl,$j3
-
-	vpmull.p64	$t0,$H2,$I2		@ H^2·Ii+2
-	veor		$j2,$j2,$I2
-	vpmull2.p64	$I2,$H2,$I2
-	vpmull2.p64	$j2,$Hhl,$j2
-
-	veor		$Yl,$Yl,$t0
-	veor		$Yh,$Yh,$I2
-	veor		$Ym,$Ym,$j2
-
-	vpmull.p64	$j3,$H3,$I1		@ H^3·Ii+1
-	veor		$j1,$j1,$I1
-	vpmull2.p64	$I1,$H3,$I1
-	vpmull.p64	$j1,$H34,$j1
-
-	veor		$Yl,$Yl,$j3
-	veor		$Yh,$Yh,$I1
-	veor		$Ym,$Ym,$j1
-
-	subs		$len,$len,#128
-	b.lo		.Ltail4x
-
-	b		.Loop4x
-
-.align	4
-.Loop4x:
-	veor		$t0,$I0,$Xl
-	 vld1.64	{$I0-$j3},[$inp],#64
-	vext.8		$IN,$t0,$t0,#8
-#ifndef __ARMEB__
-	 vrev64.8	$j1,$j1
-	 vrev64.8	$j2,$j2
-	 vrev64.8	$j3,$j3
-	 vrev64.8	$I0,$I0
-#endif
-
-	vpmull.p64	$Xl,$H4,$IN		@ H^4·(Xi+Ii)
-	veor		$t0,$t0,$IN
-	vpmull2.p64	$Xh,$H4,$IN
-	 vext.8		$I3,$j3,$j3,#8
-	vpmull2.p64	$Xm,$H34,$t0
-
-	veor		$Xl,$Xl,$Yl
-	veor		$Xh,$Xh,$Yh
-	 vext.8		$I2,$j2,$j2,#8
-	veor		$Xm,$Xm,$Ym
-	 vext.8		$I1,$j1,$j1,#8
-
-	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
-	veor		$t2,$Xl,$Xh
-	 vpmull.p64	$Yl,$H,$I3		@ H·Ii+3
-	 veor		$j3,$j3,$I3
-	veor		$Xm,$Xm,$t1
-	 vpmull2.p64	$Yh,$H,$I3
-	veor		$Xm,$Xm,$t2
-	 vpmull.p64	$Ym,$Hhl,$j3
-
-	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase of reduction
-	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
-	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
-	 vpmull.p64	$t0,$H2,$I2		@ H^2·Ii+2
-	 veor		$j2,$j2,$I2
-	 vpmull2.p64	$I2,$H2,$I2
-	veor		$Xl,$Xm,$t2
-	 vpmull2.p64	$j2,$Hhl,$j2
-
-	 veor		$Yl,$Yl,$t0
-	 veor		$Yh,$Yh,$I2
-	 veor		$Ym,$Ym,$j2
-
-	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase of reduction
-	vpmull.p64	$Xl,$Xl,$xC2
-	 vpmull.p64	$j3,$H3,$I1		@ H^3·Ii+1
-	 veor		$j1,$j1,$I1
-	veor		$t2,$t2,$Xh
-	 vpmull2.p64	$I1,$H3,$I1
-	 vpmull.p64	$j1,$H34,$j1
-
-	veor		$Xl,$Xl,$t2
-	 veor		$Yl,$Yl,$j3
-	 veor		$Yh,$Yh,$I1
-	vext.8		$Xl,$Xl,$Xl,#8
-	 veor		$Ym,$Ym,$j1
-
-	subs		$len,$len,#64
-	b.hs		.Loop4x
-
-.Ltail4x:
-	veor		$t0,$I0,$Xl
-	vext.8		$IN,$t0,$t0,#8
-
-	vpmull.p64	$Xl,$H4,$IN		@ H^4·(Xi+Ii)
-	veor		$t0,$t0,$IN
-	vpmull2.p64	$Xh,$H4,$IN
-	vpmull2.p64	$Xm,$H34,$t0
-
-	veor		$Xl,$Xl,$Yl
-	veor		$Xh,$Xh,$Yh
-	veor		$Xm,$Xm,$Ym
-
-	adds		$len,$len,#64
-	b.eq		.Ldone4x
-
-	cmp		$len,#32
-	b.lo		.Lone
-	b.eq		.Ltwo
-.Lthree:
-	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
-	veor		$t2,$Xl,$Xh
-	veor		$Xm,$Xm,$t1
-	 vld1.64	{$I0-$j2},[$inp]
-	veor		$Xm,$Xm,$t2
-#ifndef	__ARMEB__
-	 vrev64.8	$j1,$j1
-	 vrev64.8	$j2,$j2
-	 vrev64.8	$I0,$I0
-#endif
-
-	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase of reduction
-	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
-	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
-	 vext.8		$I2,$j2,$j2,#8
-	 vext.8		$I1,$j1,$j1,#8
-	veor		$Xl,$Xm,$t2
-
-	 vpmull.p64	$Yl,$H,$I2		@ H·Ii+2
-	 veor		$j2,$j2,$I2
-
-	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase of reduction
-	vpmull.p64	$Xl,$Xl,$xC2
-	veor		$t2,$t2,$Xh
-	 vpmull2.p64	$Yh,$H,$I2
-	 vpmull.p64	$Ym,$Hhl,$j2
-	veor		$Xl,$Xl,$t2
-	 vpmull.p64	$j3,$H2,$I1		@ H^2·Ii+1
-	 veor		$j1,$j1,$I1
-	vext.8		$Xl,$Xl,$Xl,#8
-
-	 vpmull2.p64	$I1,$H2,$I1
-	veor		$t0,$I0,$Xl
-	 vpmull2.p64	$j1,$Hhl,$j1
-	vext.8		$IN,$t0,$t0,#8
-
-	 veor		$Yl,$Yl,$j3
-	 veor		$Yh,$Yh,$I1
-	 veor		$Ym,$Ym,$j1
-
-	vpmull.p64	$Xl,$H3,$IN		@ H^3·(Xi+Ii)
-	veor		$t0,$t0,$IN
-	vpmull2.p64	$Xh,$H3,$IN
-	vpmull.p64	$Xm,$H34,$t0
-
-	veor		$Xl,$Xl,$Yl
-	veor		$Xh,$Xh,$Yh
-	veor		$Xm,$Xm,$Ym
-	b		.Ldone4x
-
-.align	4
-.Ltwo:
-	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
-	veor		$t2,$Xl,$Xh
-	veor		$Xm,$Xm,$t1
-	 vld1.64	{$I0-$j1},[$inp]
-	veor		$Xm,$Xm,$t2
-#ifndef	__ARMEB__
-	 vrev64.8	$j1,$j1
-	 vrev64.8	$I0,$I0
-#endif
-
-	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase of reduction
-	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
-	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
-	 vext.8		$I1,$j1,$j1,#8
-	veor		$Xl,$Xm,$t2
-
-	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase of reduction
-	vpmull.p64	$Xl,$Xl,$xC2
-	veor		$t2,$t2,$Xh
-	veor		$Xl,$Xl,$t2
-	vext.8		$Xl,$Xl,$Xl,#8
-
-	 vpmull.p64	$Yl,$H,$I1		@ H·Ii+1
-	 veor		$j1,$j1,$I1
-
-	veor		$t0,$I0,$Xl
-	vext.8		$IN,$t0,$t0,#8
-
-	 vpmull2.p64	$Yh,$H,$I1
-	 vpmull.p64	$Ym,$Hhl,$j1
-
-	vpmull.p64	$Xl,$H2,$IN		@ H^2·(Xi+Ii)
-	veor		$t0,$t0,$IN
-	vpmull2.p64	$Xh,$H2,$IN
-	vpmull2.p64	$Xm,$Hhl,$t0
-
-	veor		$Xl,$Xl,$Yl
-	veor		$Xh,$Xh,$Yh
-	veor		$Xm,$Xm,$Ym
-	b		.Ldone4x
-
-.align	4
-.Lone:
-	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
-	veor		$t2,$Xl,$Xh
-	veor		$Xm,$Xm,$t1
-	 vld1.64	{$I0},[$inp]
-	veor		$Xm,$Xm,$t2
-#ifndef	__ARMEB__
-	 vrev64.8	$I0,$I0
-#endif
-
-	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase of reduction
-	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
-	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
-	veor		$Xl,$Xm,$t2
-
-	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase of reduction
-	vpmull.p64	$Xl,$Xl,$xC2
-	veor		$t2,$t2,$Xh
-	veor		$Xl,$Xl,$t2
-	vext.8		$Xl,$Xl,$Xl,#8
-
-	veor		$t0,$I0,$Xl
-	vext.8		$IN,$t0,$t0,#8
-
-	vpmull.p64	$Xl,$H,$IN
-	veor		$t0,$t0,$IN
-	vpmull2.p64	$Xh,$H,$IN
-	vpmull.p64	$Xm,$Hhl,$t0
-
-.Ldone4x:
-	vext.8		$t1,$Xl,$Xh,#8		@ Karatsuba post-processing
-	veor		$t2,$Xl,$Xh
-	veor		$Xm,$Xm,$t1
-	veor		$Xm,$Xm,$t2
-
-	vpmull.p64	$t2,$Xl,$xC2		@ 1st phase of reduction
-	vmov		$Xh#lo,$Xm#hi		@ Xh|Xm - 256-bit result
-	vmov		$Xm#hi,$Xl#lo		@ Xm is rotated Xl
-	veor		$Xl,$Xm,$t2
-
-	vext.8		$t2,$Xl,$Xl,#8		@ 2nd phase of reduction
-	vpmull.p64	$Xl,$Xl,$xC2
-	veor		$t2,$t2,$Xh
-	veor		$Xl,$Xl,$t2
-	vext.8		$Xl,$Xl,$Xl,#8
-
-#ifndef __ARMEB__
-	vrev64.8	$Xl,$Xl
-#endif
-	vst1.64		{$Xl},[$Xi]		@ write out Xi
-
-	ret
-.size	gcm_ghash_v8_4x,.-gcm_ghash_v8_4x
-___
-
 }
-}
-
 $code.=<<___;
 .asciz  "GHASH for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
 .align  2
-#endif
 ___
 
 if ($flavour =~ /64/) {			######## 64-bit code
@@ -711,8 +359,7 @@
 	my $arg=shift;
 
 	$arg =~ m/q([0-9]+)#(lo|hi),\s*q([0-9]+)#(lo|hi)/o &&
-	sprintf	"ins	v%d.d[%d],v%d.d[%d]",$1<8?$1:$1+8,($2 eq "lo")?0:1,
-					     $3<8?$3:$3+8,($4 eq "lo")?0:1;
+	sprintf	"ins	v%d.d[%d],v%d.d[%d]",$1,($2 eq "lo")?0:1,$3,($4 eq "lo")?0:1;
     }
     foreach(split("\n",$code)) {
 	s/cclr\s+([wx])([^,]+),\s*([a-z]+)/csel	$1$2,$1zr,$1$2,$3/o	or
@@ -781,4 +428,4 @@
     }
 }
 
-close STDOUT or die "error closing STDOUT: $!"; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/modes/gcm_nohw.c b/src/crypto/fipsmodule/modes/gcm_nohw.c
index 92d5441..f8618b8 100644
--- a/src/crypto/fipsmodule/modes/gcm_nohw.c
+++ b/src/crypto/fipsmodule/modes/gcm_nohw.c
@@ -193,7 +193,7 @@
 #endif  // BORINGSSL_HAS_UINT128
 
 void gcm_init_nohw(u128 Htable[16], const uint64_t Xi[2]) {
-  // We implement GHASH in terms of POLYVAL, as described in RFC 8452. This
+  // We implement GHASH in terms of POLYVAL, as described in RFC8452. This
   // avoids a shift by 1 in the multiplication, needed to account for bit
   // reversal losing a bit after multiplication, that is,
   // rev128(X) * rev128(Y) = rev255(X*Y).
diff --git a/src/crypto/fipsmodule/modes/gcm_test.cc b/src/crypto/fipsmodule/modes/gcm_test.cc
index 02ba2d1..031b06c 100644
--- a/src/crypto/fipsmodule/modes/gcm_test.cc
+++ b/src/crypto/fipsmodule/modes/gcm_test.cc
@@ -125,7 +125,7 @@
       UINT64_C(0x66e94bd4ef8a2c3b),
       UINT64_C(0x884cfa59ca342b2e),
   };
-  static const size_t kBlockCounts[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 31, 32};
+  static const size_t kBlockCounts[] = {1, 2, 3, 4, 7, 8, 15, 16, 31, 32};
   uint8_t buf[16 * 32];
   OPENSSL_memset(buf, 42, sizeof(buf));
 
diff --git a/src/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl b/src/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl
index ac442a9..e5ff106 100644
--- a/src/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl
+++ b/src/crypto/fipsmodule/rand/asm/rdrand-x86_64.pl
@@ -84,4 +84,4 @@
 .size CRYPTO_rdrand_multiple8_buf,.-CRYPTO_rdrand_multiple8_buf
 ___
 
-close STDOUT or die "error closing STDOUT: $!";	# flush
+close STDOUT or die "error closing STDOUT";	# flush
diff --git a/src/crypto/fipsmodule/rand/rand.c b/src/crypto/fipsmodule/rand/rand.c
index 0fc9fa1..29c43ae 100644
--- a/src/crypto/fipsmodule/rand/rand.c
+++ b/src/crypto/fipsmodule/rand/rand.c
@@ -356,7 +356,7 @@
     int used_cpu;
     rand_get_seed(state, seed, &used_cpu);
 
-    uint8_t personalization[CTR_DRBG_ENTROPY_LEN] = {0};
+    uint8_t personalization[CTR_DRBG_ENTROPY_LEN];
     size_t personalization_len = 0;
 #if defined(OPENSSL_URANDOM)
     // If we used RDRAND, also opportunistically read from the system. This
diff --git a/src/crypto/fipsmodule/rsa/rsa.c b/src/crypto/fipsmodule/rsa/rsa.c
index 8f58a10..f6d3640 100644
--- a/src/crypto/fipsmodule/rsa/rsa.c
+++ b/src/crypto/fipsmodule/rsa/rsa.c
@@ -206,12 +206,6 @@
   }
 }
 
-const RSA_PSS_PARAMS *RSA_get0_pss_params(const RSA *rsa) {
-  // We do not support the id-RSASSA-PSS key encoding. If we add support later,
-  // the |maskHash| field should be filled in for OpenSSL compatibility.
-  return NULL;
-}
-
 void RSA_get0_crt_params(const RSA *rsa, const BIGNUM **out_dmp1,
                          const BIGNUM **out_dmq1, const BIGNUM **out_iqmp) {
   if (out_dmp1 != NULL) {
@@ -663,8 +657,7 @@
 }
 
 static int check_mod_inverse(int *out_ok, const BIGNUM *a, const BIGNUM *ainv,
-                             const BIGNUM *m, unsigned m_min_bits,
-                             BN_CTX *ctx) {
+                             const BIGNUM *m, BN_CTX *ctx) {
   if (BN_is_negative(ainv) || BN_cmp(ainv, m) >= 0) {
     *out_ok = 0;
     return 1;
@@ -677,7 +670,7 @@
   BIGNUM *tmp = BN_CTX_get(ctx);
   int ret = tmp != NULL &&
             bn_mul_consttime(tmp, a, ainv, ctx) &&
-            bn_div_consttime(NULL, tmp, tmp, m, m_min_bits, ctx);
+            bn_div_consttime(NULL, tmp, tmp, m, ctx);
   if (ret) {
     *out_ok = BN_is_one(tmp);
   }
@@ -757,15 +750,10 @@
   // simply check that d * e is one mod p-1 and mod q-1. Note d and e were bound
   // by earlier checks in this function.
   if (!bn_usub_consttime(&pm1, key->p, BN_value_one()) ||
-      !bn_usub_consttime(&qm1, key->q, BN_value_one())) {
-    OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
-    goto out;
-  }
-  const unsigned pm1_bits = BN_num_bits(&pm1);
-  const unsigned qm1_bits = BN_num_bits(&qm1);
-  if (!bn_mul_consttime(&de, key->d, key->e, ctx) ||
-      !bn_div_consttime(NULL, &tmp, &de, &pm1, pm1_bits, ctx) ||
-      !bn_div_consttime(NULL, &de, &de, &qm1, qm1_bits, ctx)) {
+      !bn_usub_consttime(&qm1, key->q, BN_value_one()) ||
+      !bn_mul_consttime(&de, key->d, key->e, ctx) ||
+      !bn_div_consttime(NULL, &tmp, &de, &pm1, ctx) ||
+      !bn_div_consttime(NULL, &de, &de, &qm1, ctx)) {
     OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
     goto out;
   }
@@ -784,12 +772,9 @@
 
   if (has_crt_values) {
     int dmp1_ok, dmq1_ok, iqmp_ok;
-    if (!check_mod_inverse(&dmp1_ok, key->e, key->dmp1, &pm1, pm1_bits, ctx) ||
-        !check_mod_inverse(&dmq1_ok, key->e, key->dmq1, &qm1, qm1_bits, ctx) ||
-        // |p| is odd, so |pm1| and |p| have the same bit width. If they didn't,
-        // we only need a lower bound anyway.
-        !check_mod_inverse(&iqmp_ok, key->q, key->iqmp, key->p, pm1_bits,
-                           ctx)) {
+    if (!check_mod_inverse(&dmp1_ok, key->e, key->dmp1, &pm1, ctx) ||
+        !check_mod_inverse(&dmq1_ok, key->e, key->dmq1, &qm1, ctx) ||
+        !check_mod_inverse(&iqmp_ok, key->q, key->iqmp, key->p, ctx)) {
       OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
       goto out;
     }
diff --git a/src/crypto/fipsmodule/rsa/rsa_impl.c b/src/crypto/fipsmodule/rsa/rsa_impl.c
index a6865c0..6dd89b9 100644
--- a/src/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/src/crypto/fipsmodule/rsa/rsa_impl.c
@@ -1262,14 +1262,12 @@
     // values for d.
   } while (BN_cmp(rsa->d, pow2_prime_bits) <= 0);
 
-  assert(BN_num_bits(pm1) == (unsigned)prime_bits);
-  assert(BN_num_bits(qm1) == (unsigned)prime_bits);
   if (// Calculate n.
       !bn_mul_consttime(rsa->n, rsa->p, rsa->q, ctx) ||
       // Calculate d mod (p-1).
-      !bn_div_consttime(NULL, rsa->dmp1, rsa->d, pm1, prime_bits, ctx) ||
+      !bn_div_consttime(NULL, rsa->dmp1, rsa->d, pm1, ctx) ||
       // Calculate d mod (q-1)
-      !bn_div_consttime(NULL, rsa->dmq1, rsa->d, qm1, prime_bits, ctx)) {
+      !bn_div_consttime(NULL, rsa->dmq1, rsa->d, qm1, ctx)) {
     goto bn_err;
   }
   bn_set_minimal_width(rsa->n);
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-586.pl b/src/crypto/fipsmodule/sha/asm/sha1-586.pl
index 90c2361..bf42961 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-586.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-586.pl
@@ -1483,4 +1483,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-armv4-large.pl b/src/crypto/fipsmodule/sha/asm/sha1-armv4-large.pl
index 2998b89..f11280a 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-armv4-large.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-armv4-large.pl
@@ -742,4 +742,4 @@
 	print $_,$/;
 }
 
-close STDOUT or die "error closing STDOUT: $!"; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
index 25e5234..51e326d 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
@@ -359,4 +359,4 @@
 	print $_,"\n";
 }
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-x86_64.pl b/src/crypto/fipsmodule/sha/asm/sha1-x86_64.pl
index 5126025..76e4077 100755
--- a/src/crypto/fipsmodule/sha/asm/sha1-x86_64.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-x86_64.pl
@@ -2114,4 +2114,4 @@
 
 	print $_,"\n";
 }
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha256-586.pl b/src/crypto/fipsmodule/sha/asm/sha256-586.pl
index 99b4cf1..240a604 100644
--- a/src/crypto/fipsmodule/sha/asm/sha256-586.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha256-586.pl
@@ -1287,4 +1287,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha256-armv4.pl b/src/crypto/fipsmodule/sha/asm/sha256-armv4.pl
index 0f459e0..a2eccf8 100644
--- a/src/crypto/fipsmodule/sha/asm/sha256-armv4.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha256-armv4.pl
@@ -737,4 +737,4 @@
 	print $_,"\n";
 }
 
-close STDOUT or die "error closing STDOUT: $!"; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-586.pl b/src/crypto/fipsmodule/sha/asm/sha512-586.pl
index 1aee14d..01acf67 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-586.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-586.pl
@@ -922,4 +922,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-armv4.pl b/src/crypto/fipsmodule/sha/asm/sha512-armv4.pl
index 185635f..e15b1d9 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-armv4.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-armv4.pl
@@ -672,4 +672,4 @@
 close SELF;
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!"; # enforce flush
+close STDOUT or die "error closing STDOUT"; # enforce flush
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
index ae803a9..2971b74 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
@@ -459,4 +459,4 @@
 	print $_,"\n";
 }
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-x86_64.pl b/src/crypto/fipsmodule/sha/asm/sha512-x86_64.pl
index 61f67cb..8c5a5f3 100755
--- a/src/crypto/fipsmodule/sha/asm/sha512-x86_64.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-x86_64.pl
@@ -2082,4 +2082,4 @@
 
 	print $_,"\n";
 }
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/hmac_extra/hmac_tests.txt b/src/crypto/hmac_extra/hmac_tests.txt
index 1d7efd2..53f3f8f 100644
--- a/src/crypto/hmac_extra/hmac_tests.txt
+++ b/src/crypto/hmac_extra/hmac_tests.txt
@@ -5,7 +5,7 @@
 Input = "More text test vectors to stuff up EBCDIC machines :-)"
 Output = e9139d1e6ee064ef8cf514fc7dc83e86
 
-# HMAC tests from RFC 2104
+# HMAC tests from RFC2104
 HMAC = MD5
 Key = 0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
 Input = "Hi There"
diff --git a/src/crypto/hrss/asm/poly_rq_mul.S b/src/crypto/hrss/asm/poly_rq_mul.S
index c37d7d0..53ce47c 100644
--- a/src/crypto/hrss/asm/poly_rq_mul.S
+++ b/src/crypto/hrss/asm/poly_rq_mul.S
@@ -26,6 +26,23 @@
 # This file was generated by poly_rq_mul.py
 .text
 .align 32
+mask_low9words:
+.word 0xffff
+.word 0xffff
+.word 0xffff
+.word 0xffff
+.word 0xffff
+.word 0xffff
+.word 0xffff
+.word 0xffff
+.word 0xffff
+.word 0x0
+.word 0x0
+.word 0x0
+.word 0x0
+.word 0x0
+.word 0x0
+.word 0x0
 const3:
 .word 3
 .word 3
@@ -729,20 +746,8 @@
 vmovdqu 1120(%rsi), %ymm4
 vmovdqu 1208(%rsi), %ymm5
 vmovdqu 1296(%rsi), %ymm6
-
-# Only 18 bytes more can be read, but vmovdqu reads 32.
-# Copy 18 bytes to the red zone and zero pad to 32 bytes.
-xor %r9, %r9
-movq %r9, -16(%rsp)
-movq %r9, -8(%rsp)
-movq 1384(%rsi), %r9
-movq %r9, -32(%rsp)
-movq 1384+8(%rsi), %r9
-movq %r9, -24(%rsp)
-movw 1384+16(%rsi), %r9w
-movw %r9w, -16(%rsp)
-vmovdqu -32(%rsp), %ymm7
-
+vmovdqu 1384(%rsi), %ymm7
+vpand mask_low9words(%rip), %ymm7, %ymm7
 vmovdqu 416(%rsi), %ymm8
 vmovdqu 504(%rsi), %ymm9
 vmovdqu 592(%rsi), %ymm10
@@ -1336,20 +1341,8 @@
 vmovdqu 1120(%rdx), %ymm4
 vmovdqu 1208(%rdx), %ymm5
 vmovdqu 1296(%rdx), %ymm6
-
-# Only 18 bytes more can be read, but vmovdqu reads 32.
-# Copy 18 bytes to the red zone and zero pad to 32 bytes.
-xor %r9, %r9
-movq %r9, -16(%rsp)
-movq %r9, -8(%rsp)
-movq 1384(%rdx), %r9
-movq %r9, -32(%rsp)
-movq 1384+8(%rdx), %r9
-movq %r9, -24(%rsp)
-movw 1384+16(%rdx), %r9w
-movw %r9w, -16(%rsp)
-vmovdqu -32(%rsp), %ymm7
-
+vmovdqu 1384(%rdx), %ymm7
+vpand mask_low9words(%rip), %ymm7, %ymm7
 vmovdqu 416(%rdx), %ymm8
 vmovdqu 504(%rdx), %ymm9
 vmovdqu 592(%rdx), %ymm10
@@ -8302,20 +8295,7 @@
 vmovdqa %ymm8, 2880(%r8)
 vmovdqu 680(%rdi), %ymm8
 vmovdqu 1032(%rdi), %ymm10
-
-# Only 18 bytes can be read at 1384, but vmovdqu reads 32.
-# Copy 18 bytes to the red zone and zero pad to 32 bytes.
-xor %r9, %r9
-movq %r9, -16(%rsp)
-movq %r9, -8(%rsp)
-movq 1384(%rdi), %r9
-movq %r9, -32(%rsp)
-movq 1384+8(%rdi), %r9
-movq %r9, -24(%rsp)
-movw 1384+16(%rdi), %r9w
-movw %r9w, -16(%rsp)
-vmovdqu -32(%rsp), %ymm2
-
+vmovdqu 1384(%rdi), %ymm2
 vpaddw %ymm5, %ymm8, %ymm5
 vpaddw %ymm6, %ymm10, %ymm6
 vpaddw %ymm4, %ymm2, %ymm4
diff --git a/src/crypto/mem.c b/src/crypto/mem.c
index 8988937..883439b 100644
--- a/src/crypto/mem.c
+++ b/src/crypto/mem.c
@@ -324,15 +324,22 @@
 }
 
 char *OPENSSL_strndup(const char *str, size_t size) {
+  char *ret;
+  size_t alloc_size;
+
+  if (str == NULL) {
+    return NULL;
+  }
+
   size = OPENSSL_strnlen(str, size);
 
-  size_t alloc_size = size + 1;
+  alloc_size = size + 1;
   if (alloc_size < size) {
     // overflow
     OPENSSL_PUT_ERROR(CRYPTO, ERR_R_MALLOC_FAILURE);
     return NULL;
   }
-  char *ret = OPENSSL_malloc(alloc_size);
+  ret = OPENSSL_malloc(alloc_size);
   if (ret == NULL) {
     OPENSSL_PUT_ERROR(CRYPTO, ERR_R_MALLOC_FAILURE);
     return NULL;
diff --git a/src/crypto/obj/objects.txt b/src/crypto/obj/objects.txt
index 25c2430..b88342d 100644
--- a/src/crypto/obj/objects.txt
+++ b/src/crypto/obj/objects.txt
@@ -364,7 +364,7 @@
 rsadsi 2 6		:			: hmacWithMD5
 rsadsi 2 7		:			: hmacWithSHA1
 
-# From RFC 4231
+# From RFC4231
 rsadsi 2 8		:			: hmacWithSHA224
 rsadsi 2 9		:			: hmacWithSHA256
 rsadsi 2 10		:			: hmacWithSHA384
@@ -492,7 +492,7 @@
 id-kp 7			: ipsecUser		: IPSec User
 !Cname time-stamp
 id-kp 8			: timeStamping		: Time Stamping
-# From OCSP spec RFC 2560
+# From OCSP spec RFC2560
 !Cname OCSP-sign
 id-kp 9			: OCSPSigning		: OCSP Signing
 id-kp 10		: DVCS			: dvcs
@@ -776,7 +776,7 @@
 !Cname no-rev-avail
 id-ce 56		: noRevAvail		: X509v3 No Revocation Available
 
-# From RFC 5280
+# From RFC5280
 ext-key-usage 0		: anyExtendedKeyUsage	: Any Extended Key Usage
 
 
@@ -820,7 +820,7 @@
 internet 5		: security		: Security
 internet 6		: snmpv2		: SNMPv2
 # Documents refer to "internet 7" as "mail". This however leads to ambiguities
-# with RFC 2798, Section 9.1.3, where "mail" is defined as the short name for
+# with RFC2798, Section 9.1.3, where "mail" is defined as the short name for
 # rfc822Mailbox. The short name is therefore here left out for a reason.
 # Subclasses of "mail", e.g. "MIME MHS" don't consitute a problem, as
 # references are realized via long name "Mail" (with capital M).
@@ -1313,7 +1313,7 @@
 1 3 36 3 3 2 8 1 1 13 : brainpoolP512r1
 1 3 36 3 3 2 8 1 1 14 : brainpoolP512t1
 
-# ECDH schemes from RFC 5753
+# ECDH schemes from RFC5753
 !Alias x9-63-scheme 1 3 133 16 840 63 0
 !Alias secg-scheme certicom-arc 1
 
diff --git a/src/crypto/pem/pem_all.c b/src/crypto/pem/pem_all.c
index e419774..6b40883 100644
--- a/src/crypto/pem/pem_all.c
+++ b/src/crypto/pem/pem_all.c
@@ -157,6 +157,8 @@
     return pkey_get_rsa(pktmp, rsa);
 }
 
+#ifndef OPENSSL_NO_FP_API
+
 RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u)
 {
     EVP_PKEY *pktmp;
@@ -164,6 +166,8 @@
     return pkey_get_rsa(pktmp, rsa);
 }
 
+#endif
+
 IMPLEMENT_PEM_write_cb_const(RSAPrivateKey, RSA, PEM_STRING_RSA,
                              RSAPrivateKey)
 
@@ -201,6 +205,7 @@
                              DSAPrivateKey)
 
     IMPLEMENT_PEM_rw(DSA_PUBKEY, DSA, PEM_STRING_PUBLIC, DSA_PUBKEY)
+# ifndef OPENSSL_NO_FP_API
 DSA *PEM_read_DSAPrivateKey(FILE *fp, DSA **dsa, pem_password_cb *cb, void *u)
 {
     EVP_PKEY *pktmp;
@@ -208,6 +213,8 @@
     return pkey_get_dsa(pktmp, dsa); /* will free pktmp */
 }
 
+# endif
+
 IMPLEMENT_PEM_rw_const(DSAparams, DSA, PEM_STRING_DSAPARAMS, DSAparams)
 #endif
 static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey)
@@ -238,6 +245,7 @@
                        ECPrivateKey)
 
     IMPLEMENT_PEM_rw(EC_PUBKEY, EC_KEY, PEM_STRING_PUBLIC, EC_PUBKEY)
+#ifndef OPENSSL_NO_FP_API
 EC_KEY *PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb,
                               void *u)
 {
@@ -246,6 +254,7 @@
     return pkey_get_eckey(pktmp, eckey); /* will free pktmp */
 }
 
+#endif
 
 IMPLEMENT_PEM_write_const(DHparams, DH, PEM_STRING_DHPARAMS, DHparams)
 
diff --git a/src/crypto/pem/pem_info.c b/src/crypto/pem/pem_info.c
index 3a1d0cc..1cda35b 100644
--- a/src/crypto/pem/pem_info.c
+++ b/src/crypto/pem/pem_info.c
@@ -70,6 +70,7 @@
 #include <openssl/rsa.h>
 #include <openssl/x509.h>
 
+#ifndef OPENSSL_NO_FP_API
 STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,
                                         pem_password_cb *cb, void *u)
 {
@@ -82,6 +83,7 @@
     BIO_free(b);
     return ret;
 }
+#endif
 
 enum parse_result_t {
     parse_ok,
diff --git a/src/crypto/pem/pem_lib.c b/src/crypto/pem/pem_lib.c
index 747d694..00c0e0a 100644
--- a/src/crypto/pem/pem_lib.c
+++ b/src/crypto/pem/pem_lib.c
@@ -117,6 +117,7 @@
     buf[j + i * 2 + 1] = '\0';
 }
 
+#ifndef OPENSSL_NO_FP_API
 void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
                     pem_password_cb *cb, void *u)
 {
@@ -129,6 +130,7 @@
     BIO_free(b);
     return ret;
 }
+#endif
 
 static int check_pem(const char *nm, const char *name)
 {
@@ -250,6 +252,7 @@
     return ret;
 }
 
+#ifndef OPENSSL_NO_FP_API
 int PEM_ASN1_write(i2d_of_void *i2d, const char *name, FILE *fp,
                    void *x, const EVP_CIPHER *enc, unsigned char *kstr,
                    int klen, pem_password_cb *callback, void *u)
@@ -263,6 +266,7 @@
     BIO_free(b);
     return ret;
 }
+#endif
 
 int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
                        void *x, const EVP_CIPHER *enc, unsigned char *kstr,
@@ -503,6 +507,7 @@
     return (1);
 }
 
+#ifndef OPENSSL_NO_FP_API
 int PEM_write(FILE *fp, const char *name, const char *header,
               const unsigned char *data, long len)
 {
@@ -515,6 +520,7 @@
     BIO_free(b);
     return (ret);
 }
+#endif
 
 int PEM_write_bio(BIO *bp, const char *name, const char *header,
                   const unsigned char *data, long len)
@@ -572,6 +578,7 @@
     return (0);
 }
 
+#ifndef OPENSSL_NO_FP_API
 int PEM_read(FILE *fp, char **name, char **header, unsigned char **data,
              long *len)
 {
@@ -584,6 +591,7 @@
     BIO_free(b);
     return (ret);
 }
+#endif
 
 int PEM_read_bio(BIO *bp, char **name, char **header, unsigned char **data,
                  long *len)
diff --git a/src/crypto/pem/pem_pk8.c b/src/crypto/pem/pem_pk8.c
index 8a1f040..819a329 100644
--- a/src/crypto/pem/pem_pk8.c
+++ b/src/crypto/pem/pem_pk8.c
@@ -190,6 +190,7 @@
     return ret;
 }
 
+#ifndef OPENSSL_NO_FP_API
 
 int i2d_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
                            char *kstr, int klen, pem_password_cb *cb, void *u)
@@ -247,6 +248,7 @@
     return ret;
 }
 
+#endif
 
 IMPLEMENT_PEM_rw(PKCS8, X509_SIG, PEM_STRING_PKCS8, X509_SIG)
 
diff --git a/src/crypto/pem/pem_pkey.c b/src/crypto/pem/pem_pkey.c
index 48d8c96..5776535 100644
--- a/src/crypto/pem/pem_pkey.c
+++ b/src/crypto/pem/pem_pkey.c
@@ -150,6 +150,7 @@
     return PEM_write_bio_PKCS8PrivateKey(bp, x, enc, (char *)kstr, klen, cb, u);
 }
 
+#ifndef OPENSSL_NO_FP_API
 EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
                               void *u)
 {
@@ -177,6 +178,7 @@
     return ret;
 }
 
+#endif
 
 /* Transparently read in PKCS#3 or X9.42 DH parameters */
 
@@ -201,6 +203,7 @@
     return ret;
 }
 
+#ifndef OPENSSL_NO_FP_API
 DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u)
 {
     BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
@@ -212,3 +215,4 @@
     BIO_free(b);
     return ret;
 }
+#endif
diff --git a/src/crypto/perlasm/arm-xlate.pl b/src/crypto/perlasm/arm-xlate.pl
index c000e02..576f682 100755
--- a/src/crypto/perlasm/arm-xlate.pl
+++ b/src/crypto/perlasm/arm-xlate.pl
@@ -245,4 +245,4 @@
 # See https://www.airs.com/blog/archives/518.
 print ".section\t.note.GNU-stack,\"\",\%progbits\n" if ($flavour =~ /linux/);
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT;
diff --git a/src/crypto/perlasm/ppc-xlate.pl b/src/crypto/perlasm/ppc-xlate.pl
index d62ae01..4f22c36 100644
--- a/src/crypto/perlasm/ppc-xlate.pl
+++ b/src/crypto/perlasm/ppc-xlate.pl
@@ -314,4 +314,4 @@
 # See https://www.airs.com/blog/archives/518.
 print ".section\t.note.GNU-stack,\"\",\@progbits\n" if ($flavour =~ /linux/);
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT;
diff --git a/src/crypto/perlasm/x86_64-xlate.pl b/src/crypto/perlasm/x86_64-xlate.pl
index 04abd0b..4a41a24 100755
--- a/src/crypto/perlasm/x86_64-xlate.pl
+++ b/src/crypto/perlasm/x86_64-xlate.pl
@@ -1264,7 +1264,7 @@
 # See https://www.airs.com/blog/archives/518.
 print ".section\t.note.GNU-stack,\"\",\@progbits\n" if ($elf);
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT;
 
 #################################################
 # Cross-reference x86_64 ABI "card"
diff --git a/src/crypto/pkcs7/internal.h b/src/crypto/pkcs7/internal.h
index 5ee8e8a..9541bea 100644
--- a/src/crypto/pkcs7/internal.h
+++ b/src/crypto/pkcs7/internal.h
@@ -32,23 +32,14 @@
 // NULL.
 int pkcs7_parse_header(uint8_t **der_bytes, CBS *out, CBS *cbs);
 
-// pkcs7_add_signed_data writes a PKCS#7, SignedData structure to |out|. While
-// doing so it makes callbacks to let the caller fill in parts of the structure.
-// All callbacks are ignored if NULL and return one on success or zero on error.
+// pkcs7_bundle writes a PKCS#7, SignedData structure to |out| and then calls
+// |cb| with a CBB to which certificate or CRL data can be written, and the
+// opaque context pointer, |arg|. The callback can return zero to indicate an
+// error.
 //
-//   digest_algos_cb: may write AlgorithmIdentifiers into the given CBB, which
-//       is a SET of digest algorithms.
-//   cert_crl_cb: may write the |certificates| or |crls| fields.
-//       (See https://datatracker.ietf.org/doc/html/rfc2315#section-9.1)
-//   signer_infos_cb: may write the contents of the |signerInfos| field.
-//       (See https://datatracker.ietf.org/doc/html/rfc2315#section-9.1)
-//
-// pkcs7_add_signed_data returns one on success or zero on error.
-int pkcs7_add_signed_data(CBB *out,
-                          int (*digest_algos_cb)(CBB *out, const void *arg),
-                          int (*cert_crl_cb)(CBB *out, const void *arg),
-                          int (*signer_infos_cb)(CBB *out, const void *arg),
-                          const void *arg);
+// pkcs7_bundle returns one on success or zero on error.
+int pkcs7_bundle(CBB *out, int (*cb)(CBB *out, const void *arg),
+                 const void *arg);
 
 
 #if defined(__cplusplus)
diff --git a/src/crypto/pkcs7/pkcs7.c b/src/crypto/pkcs7/pkcs7.c
index 6be5a07..1d0b139 100644
--- a/src/crypto/pkcs7/pkcs7.c
+++ b/src/crypto/pkcs7/pkcs7.c
@@ -131,11 +131,8 @@
   return ret;
 }
 
-int pkcs7_add_signed_data(CBB *out,
-                          int (*digest_algos_cb)(CBB *out, const void *arg),
-                          int (*cert_crl_cb)(CBB *out, const void *arg),
-                          int (*signer_infos_cb)(CBB *out, const void *arg),
-                          const void *arg) {
+int pkcs7_bundle(CBB *out, int (*cb)(CBB *out, const void *arg),
+                 const void *arg) {
   CBB outer_seq, oid, wrapped_seq, seq, version_bytes, digest_algos_set,
       content_info, signer_infos;
 
@@ -150,13 +147,11 @@
       !CBB_add_asn1(&seq, &version_bytes, CBS_ASN1_INTEGER) ||
       !CBB_add_u8(&version_bytes, 1) ||
       !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
-      (digest_algos_cb != NULL && !digest_algos_cb(&digest_algos_set, arg)) ||
       !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
       !CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
-      (cert_crl_cb != NULL && !cert_crl_cb(&seq, arg)) ||
-      !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
-      (signer_infos_cb != NULL && !signer_infos_cb(&signer_infos, arg))) {
+      !cb(&seq, arg) ||
+      !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET)) {
     return 0;
   }
 
diff --git a/src/crypto/pkcs7/pkcs7_test.cc b/src/crypto/pkcs7/pkcs7_test.cc
index 9cca175..8e14603 100644
--- a/src/crypto/pkcs7/pkcs7_test.cc
+++ b/src/crypto/pkcs7/pkcs7_test.cc
@@ -775,201 +775,3 @@
   check_order(crl1.get(), crl2.get());
   check_order(crl2.get(), crl1.get());
 }
-
-TEST(PKCS7Test, KernelModuleSigning) {
-  // Sign a message with the same call that the Linux kernel's sign-file.c
-  // makes.
-  static const char kCert[] = R"(
------BEGIN CERTIFICATE-----
-MIIFazCCA1OgAwIBAgIURVkPzF/4dwy7419Qk75uhIuyf0EwDQYJKoZIhvcNAQEL
-BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
-GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMTA5MjExOTIyMTJaFw0yMjA5
-MjExOTIyMTJaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw
-HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIiMA0GCSqGSIb3DQEB
-AQUAA4ICDwAwggIKAoICAQC1+MOn+BopcEVR4QMvjXdAxGkWFllXyQFDToL+qOiP
-RU1yN7C8KCtkbOAFttJIO4O/i0iZ7KqYbnmB6YUA/ONAcakocnrdoESgRJcVMeAx
-Dk/11OtMF5yIfeOOO/TUeVNmAUaT63gFbKy/adpqhzJtOv9BBl5VcYNGGSE+0wtb
-mjpmNsxunEQR1KLDc97fGYHeRfKoSyrCIEE8IaAEpKGR2Sku3v9Jwh7RpjupgiUA
-kH6pJk7VMZm5vl2wFjYvfysgjeN5ZtsxFDMaPYZStpxMxpNd5C9DsO2Ljp5NMpGf
-NGmG4ZqiaQg8z2cIM6ESmN1zDJdUh5IXed1fOxBZD/poUFH0wDRFWnvzlaPmjJEF
-rYLMK8svnE5nEQp9vu93ISFBx7cofs+niMaUXPEqaRSqruifN2M1it3kOf/8YZl1
-vurs+VtHD6nOJo6bd11+37aBidIB/BaWnzLrDmSTcPFa1tkTHwoLqc9+jThTq9jZ
-6w3lAMPpsoenyD19UmQB589+4kNp2SIO/TtzVQCGgQPXE2jDCl6G9aIPMkfvpPZK
-4THVil3WQRCFYnYdDO4HQXo2ZuC4RiqgY5ygfeoL+fa9k383lgxxAHQLS7xsbaVB
-40RmfdbdevgPYIwZNNO78ddRmMdSv6IknSW9gydGzY//btY+t1SWcBZWzn1Ewq8g
-2QIDAQABo1MwUTAdBgNVHQ4EFgQUotZD9ajEvnQYVezIWzcW4pzvMcUwHwYDVR0j
-BBgwFoAUotZD9ajEvnQYVezIWzcW4pzvMcUwDwYDVR0TAQH/BAUwAwEB/zANBgkq
-hkiG9w0BAQsFAAOCAgEAqCe42PIWoyLDx9bR+5cSp99N5xo5lLiSLtWx2emDbZB2
-AunqKYeEgIV+TWNF2w1SZ/ckFgV7SlL2Yl73N/veSNRfNAnpjLksGDFpdJb7YXrx
-cUvxdy1mr8oau6J7PC9JGjBTBrnhqwCQX1FtcAxODKll2Lsfuj6+bdC3rCK7KBEo
-ENamMJZIeo8lRP9qFF2xwCEzZjRv2zvB6O5o9045aTUcdCrwUfKE2sqY6EXRzFTC
-waK0HRCd1FLv9omhz/Ug5PMHP4d6MZfnAbFm+AzAhnpkrk/9TJYSOoNTNLWsuqhp
-dN0rKqiFWv1zIwfknXvTh1P1Ap+G5jffAca0zWUH1oKjE7ZZioSsaZ6gySnD8+WQ
-TPbOYtG+n0mhCH1TrU8Dqi3rd8g5IbC8loYLRH94QtodOnevD4Qo9Orfrsr8hGOW
-ABespanZArhoQ03DAtpNhtHm2NWJQF2uHNqcTrkq0omqZBTbMD1GKMBujoNooAUu
-w51U9r+RycPJTFqEGHb0nd7EjoyXEXtuX1Ld5fTZjQ9SszmQKQ8w3lHqRGNlkSiO
-e3IOOq2ruXmq1jykxpmi82IcTRUE8TZBfL/yz0nxpHKAYC1VwMezrkgZDGz4npxf
-1z2+qd58xU6/jsf7/+3xdPFubeEJujdbCkWQsQC5Rzm48zDWGq/pyzFji44K3TA=
------END CERTIFICATE-----
-)";
-
-  static const char kKey[] = R"(
------BEGIN PRIVATE KEY-----
-MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC1+MOn+BopcEVR
-4QMvjXdAxGkWFllXyQFDToL+qOiPRU1yN7C8KCtkbOAFttJIO4O/i0iZ7KqYbnmB
-6YUA/ONAcakocnrdoESgRJcVMeAxDk/11OtMF5yIfeOOO/TUeVNmAUaT63gFbKy/
-adpqhzJtOv9BBl5VcYNGGSE+0wtbmjpmNsxunEQR1KLDc97fGYHeRfKoSyrCIEE8
-IaAEpKGR2Sku3v9Jwh7RpjupgiUAkH6pJk7VMZm5vl2wFjYvfysgjeN5ZtsxFDMa
-PYZStpxMxpNd5C9DsO2Ljp5NMpGfNGmG4ZqiaQg8z2cIM6ESmN1zDJdUh5IXed1f
-OxBZD/poUFH0wDRFWnvzlaPmjJEFrYLMK8svnE5nEQp9vu93ISFBx7cofs+niMaU
-XPEqaRSqruifN2M1it3kOf/8YZl1vurs+VtHD6nOJo6bd11+37aBidIB/BaWnzLr
-DmSTcPFa1tkTHwoLqc9+jThTq9jZ6w3lAMPpsoenyD19UmQB589+4kNp2SIO/Ttz
-VQCGgQPXE2jDCl6G9aIPMkfvpPZK4THVil3WQRCFYnYdDO4HQXo2ZuC4RiqgY5yg
-feoL+fa9k383lgxxAHQLS7xsbaVB40RmfdbdevgPYIwZNNO78ddRmMdSv6IknSW9
-gydGzY//btY+t1SWcBZWzn1Ewq8g2QIDAQABAoICAFQ/liZAIaypxA5ChP0RG/Mq
-fBSzyC1ybFlDEjbg8LrUNST6T6LtXhmipp0+pWC33SljTPumrNzh2POir+djLbt6
-Y/zL88KEHwGsf95aNxe/Lpn8N+wEyn4O+rmxXIq6mTgSwyBc1jZ8uAXu9iZ37YrQ
-07jBQA+C/GoJ3HB/uTRx1TPZjxBu3Lz8m1auYLMd1hiYfd4Y3vT9hfZXAwTjS8KA
-riZ7K+p0K1yY/+pczNDUFTAvAjSGQEvUrP+HaRLYZ5ks1/IvArBYT8iIT5Yf4YFS
-NowzxwYp9fC02OmYzf7Nf0XpUXR7+EpfI66SaLJ5f51yaOXD1olz7F/YsprpYN7+
-oQd7EKar1bY3ROM6naUZtsIoEblg6B0mkyHWQgZ9wZRbcN7Zmuc/tIpLat7se+MP
-xQeAcH4Yhgnd2G6EELpmJBcyJ0Ss3atpI1eenU+ly++L4XbDQH9norKQ1PEDXYbV
-XMAV5uIsplBL7hGIa6/u/cRMM5eN3TJchtzIHFhq9+ENMvjTOfo0bflcYR+tNxGD
-6agWlD/Apedaapu/3Xp7ekyCiy/YTIwgT4U3rprYplzFM5HbzYtZ9ThxUm+CmnYj
-ZSCKiLoaQq+11/M9zH1Je0uJP5aK0CxOii2LVRXZYaQfbDtiHNWUSM7uPIZMnDgE
-IPTpl9CEfk7U3pgiUlg5AoIBAQDjUeikACPaRuewIjLqwTT2/j+ZO+/dCG4atFZa
-W+gdZ1NVDCdowQPBZWg6bqejRr1MvORg2L83kqZDQjaT9y59qxsFhXCy26xKp7aP
-Z4pEvUQmQnnf3RYHk3EBtOHyyMetTaghTGzL3MlPGo3uGbCiYtVoPKXZXGWeiOFN
-s9RNDh/7m6harB2bmX2cK+QPdJ1roVBXQDLkjh2mvLnC5vrsw81GWSkbWQpYmnVi
-YdLhytM+UTYjTrSugtrKk9e2KOFf2uR8PVaPeINEM4uubxW5YUy6gwF8ePtWYAtZ
-Skw3kdBdShhGzHORSY3NsRTJZL6AUdkhHYFTl/rlfj1WXsdnAoIBAQDM7i0u2T+E
-HmroTGiQAIRUEwUZQFDRkcEnM75jpkQT39jXF+zmhjzS1slJF2x0E0jUBV0juVWh
-mz1kHjTMV0j3/mvCeVv0iTcdIbHYRtTwmOjzkwTsZGh6T7okYck3KexRjpyhPpcX
-hOHOPJKS/muG0ZuaJjTEbJOzrSPU0rt0ppL7nOwd5jIOoGAciWiP17G1Lyyitrv4
-mKBK6mFQQWjAgEGy3jvBocbUo7Qo8Aucm6Y4eF1fUyC/X07RBzERHS4TuM+AQlDN
-T+LgTgcwTjE+Nzow2WMwCIbhVQqFRScuWqcJ6NQ6S/dV0R+aGJ90Ey+DtiZ9N9uV
-j0omAGvM8u2/AoIBADXF94FsIw8MfNw2itLrl2riJAtMmWYxC1K33EGNwi/KdHUG
-5f+qwQerxGcmK/O81STk/iVGwJ0VzMzWSfDgpRfHNSIuOcWln3EdkVsFBDlUiF2A
-ljH1q7NpFm9v6Y80HcAKQb52xLnI5boXrwFnBFi1hoQc7KKpb8R73sgxxQPhVoF/
-hejFFE/tlEAwRce+L0r5ovaw0hks4SjDNjI7z5nYi6ObjdTRUFg7WY9HUspk32m7
-blIV2Tn67GTFal7F9uJk9m3JWMOhn3OvudguoPX0ZWEtgll+iP4axDSAFd2DWcXn
-tCxzStdQjgHdZOxrL4FNW06xGxm6Nvi4zyuySfsCggEAOuIpC3ATBxRyZYMm/FGZ
-tEquyV2omz8FQA1nJFzu7MMCHHPcdzSVH4Pl3GGloQi1gW51H8GuMDxZ/H2NcDWY
-WuG49u1GFdKjinRXFKztnKBjNzHEVWRYfOSRuMh8N6SNKbYPnWlNos1k0IypFSGT
-pe5uhnF58gK8wgD67bkLce43B6NEWSb+tSMx2qFE8SfqAQSoD6zv//NjA4OrKJNS
-1RVFS279vpqMdib/qk+nFn3G2i0Dr1NEcpihHgCyAZff2Hze6pyjeQr+RrNE74VY
-MudNiiG8lV2t2+tClZ6ULoaPvpIvAP04+WiYav+uOX0VxwO8tXgqWSQOCzNNxlr7
-IwKCAQA7odNjE6Sc2qiecrOu13kEi3gT0heshIyZ0XhePrS1vgHfCouIRvNMw4FT
-45ZZUFDSdOxhrew5GuMeLvo2YILBjmkX3UqTojQMbur7FcGH8/P0Sm0f20Vc06oS
-sQF5Ji4LSyf6t9oQKePjFIGoIc6pf6BXJZYP4rBnzQzUQjH2yzDYDY3TuV7bFJJU
-DcSTGM6nP0fRMmgBtB14o7A6Gsy6X/N2ElgbvWT8YhmUC6H8DIzmZwHRKaG6C6g5
-eEjuAYenYNM4jxeteC1neUDIdGxH/BA7JrAqcGaN9GT+R47YIfiS2WrEssD1Pi5h
-hJTbHtjEDJ7BHLC/CNUhXbpyyu1y
------END PRIVATE KEY-----
-)";
-
-  bssl::UniquePtr<BIO> cert_bio(
-      BIO_new_mem_buf(const_cast<char *>(kCert), sizeof(kCert) - 1));
-  bssl::UniquePtr<X509> cert(
-      PEM_read_bio_X509(cert_bio.get(), nullptr, nullptr, nullptr));
-
-  bssl::UniquePtr<BIO> key_bio(
-      BIO_new_mem_buf(const_cast<char *>(kKey), sizeof(kKey) - 1));
-  bssl::UniquePtr<EVP_PKEY> key(
-      PEM_read_bio_PrivateKey(key_bio.get(), nullptr, nullptr, nullptr));
-
-  static const char kSignedData[] = "signed data";
-  bssl::UniquePtr<BIO> data_bio(BIO_new_mem_buf(const_cast<char *>(kSignedData),
-                                                sizeof(kSignedData) - 1));
-
-  bssl::UniquePtr<PKCS7> pkcs7(
-      PKCS7_sign(cert.get(), key.get(), /*certs=*/nullptr, data_bio.get(),
-                 PKCS7_NOATTR | PKCS7_BINARY | PKCS7_NOCERTS | PKCS7_DETACHED));
-  ASSERT_TRUE(pkcs7);
-
-  uint8_t *pkcs7_bytes = nullptr;
-  const int pkcs7_len = i2d_PKCS7(pkcs7.get(), &pkcs7_bytes);
-  ASSERT_GE(pkcs7_len, 0);
-  bssl::UniquePtr<uint8_t> pkcs7_storage(pkcs7_bytes);
-
-  // RSA signatures are deterministic so the output should not change.
-  static const uint8_t kExpectedOutput[] = {
-      0x30, 0x82, 0x02, 0xbc, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-      0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xad, 0x30, 0x82, 0x02, 0xa9, 0x02,
-      0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
-      0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48,
-      0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, 0x86, 0x30, 0x82,
-      0x02, 0x82, 0x02, 0x01, 0x01, 0x30, 0x5d, 0x30, 0x45, 0x31, 0x0b, 0x30,
-      0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
-      0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d,
-      0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
-      0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
-      0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
-      0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x02, 0x14, 0x45, 0x59, 0x0f, 0xcc,
-      0x5f, 0xf8, 0x77, 0x0c, 0xbb, 0xe3, 0x5f, 0x50, 0x93, 0xbe, 0x6e, 0x84,
-      0x8b, 0xb2, 0x7f, 0x41, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
-      0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
-      0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00,
-      0x54, 0xd4, 0x7c, 0xdc, 0x19, 0x86, 0xa1, 0xb2, 0xbe, 0xe3, 0xa4, 0x5e,
-      0xad, 0x16, 0x6f, 0x7c, 0xf9, 0xa6, 0x40, 0x90, 0xb8, 0x78, 0x85, 0xf1,
-      0x02, 0x59, 0xe5, 0x9f, 0x83, 0xfb, 0x20, 0xcf, 0x29, 0x52, 0xb6, 0x35,
-      0x5c, 0xf9, 0xef, 0x4e, 0xc5, 0xd3, 0xa6, 0x45, 0x6e, 0xfa, 0x0a, 0xa7,
-      0x53, 0xc8, 0xf4, 0xf9, 0xd6, 0xc5, 0xd8, 0xd8, 0x04, 0x3d, 0xb4, 0x15,
-      0xa7, 0x7a, 0x53, 0xdd, 0x27, 0xfa, 0x58, 0x2e, 0x5e, 0xc4, 0xcd, 0x45,
-      0xaa, 0xc2, 0x7b, 0xf9, 0x3d, 0xd7, 0x22, 0x20, 0x90, 0xbb, 0xa5, 0x62,
-      0xd5, 0xaa, 0x39, 0x8f, 0xc1, 0x00, 0xef, 0x4b, 0x03, 0x2c, 0x32, 0xc0,
-      0xad, 0x27, 0xb6, 0xfe, 0x86, 0xe5, 0x9d, 0xf0, 0xbe, 0xb1, 0x0d, 0xa4,
-      0xa3, 0x40, 0xe0, 0xaa, 0x0a, 0x13, 0x6e, 0x61, 0x9a, 0x3b, 0xae, 0x78,
-      0xd4, 0x6f, 0x2d, 0x1d, 0x40, 0x4b, 0xe3, 0x5f, 0xf8, 0xe8, 0x21, 0x89,
-      0x35, 0x73, 0x6d, 0x7e, 0x41, 0xc6, 0x0f, 0x0c, 0x01, 0x64, 0x61, 0xa8,
-      0x37, 0xef, 0x2b, 0x95, 0xb7, 0x34, 0xac, 0xc7, 0xdb, 0x66, 0x87, 0x45,
-      0xb4, 0x0f, 0x60, 0x01, 0x07, 0x29, 0x74, 0x32, 0x0b, 0xae, 0xbc, 0x08,
-      0x88, 0x15, 0xc3, 0x79, 0x4a, 0x1c, 0x5a, 0x9c, 0xc2, 0xfb, 0x4f, 0xd3,
-      0x17, 0xc2, 0x40, 0x71, 0x37, 0xea, 0xa6, 0x1e, 0xf0, 0x5b, 0xa5, 0xd7,
-      0x9b, 0x9e, 0x57, 0x44, 0x74, 0xc5, 0xd5, 0x5f, 0xba, 0xbc, 0xd7, 0xe1,
-      0xae, 0xd0, 0xd3, 0xb5, 0x10, 0xc6, 0x8b, 0xb1, 0x83, 0x7c, 0xaa, 0x3a,
-      0xbb, 0xe8, 0x7f, 0x56, 0xc4, 0x3b, 0x9d, 0x45, 0x09, 0x9b, 0x34, 0xc9,
-      0xfb, 0x5a, 0xa1, 0xab, 0xd0, 0x07, 0x79, 0x43, 0x58, 0x44, 0xd7, 0x40,
-      0xc4, 0xa7, 0xd3, 0xe9, 0x18, 0xb9, 0x78, 0x1d, 0x93, 0x0b, 0xc1, 0xdb,
-      0xc3, 0xae, 0xc9, 0xe8, 0x2c, 0xa7, 0x8c, 0x7e, 0x31, 0x1e, 0xec, 0x1c,
-      0xab, 0x83, 0xa0, 0x5d, 0x0e, 0xc3, 0x6a, 0x7c, 0x97, 0x09, 0xcf, 0x00,
-      0xa9, 0x66, 0xda, 0x21, 0x85, 0xaa, 0x47, 0xd8, 0xea, 0x8f, 0x72, 0x54,
-      0x03, 0x6c, 0xbc, 0x4b, 0xf9, 0x92, 0xae, 0x82, 0x75, 0x33, 0x10, 0x4d,
-      0x65, 0x4d, 0x0e, 0x73, 0x5d, 0x6f, 0x09, 0xee, 0x56, 0x78, 0x87, 0x0b,
-      0xa3, 0xaa, 0xc2, 0x5f, 0x49, 0x73, 0x0d, 0x78, 0xfa, 0x40, 0xc1, 0x25,
-      0x2f, 0x5d, 0x8a, 0xe1, 0xbf, 0x38, 0x2c, 0xd0, 0x26, 0xbd, 0xf5, 0x6e,
-      0x02, 0x01, 0x2e, 0x9e, 0x27, 0x64, 0x4b, 0x61, 0x8c, 0x68, 0x6e, 0x09,
-      0xfe, 0x0b, 0xf8, 0x36, 0x4e, 0x84, 0xb7, 0x76, 0xcb, 0x41, 0xf0, 0x40,
-      0x72, 0xc9, 0x74, 0x64, 0x5f, 0xbe, 0x9e, 0xfe, 0x9e, 0xce, 0x89, 0x84,
-      0x68, 0x81, 0x57, 0x2a, 0xdb, 0xd6, 0x01, 0xa8, 0x1b, 0x6e, 0x5d, 0xc4,
-      0x65, 0xbd, 0x0d, 0x98, 0x54, 0xa3, 0x18, 0x23, 0x09, 0x4a, 0x8d, 0x6c,
-      0xc6, 0x2e, 0xfe, 0x7a, 0xa9, 0x11, 0x92, 0x8b, 0xd0, 0xc1, 0xe7, 0x76,
-      0x71, 0xec, 0x34, 0xfc, 0xc8, 0x2a, 0x5e, 0x38, 0x52, 0xe6, 0xc8, 0xa5,
-      0x1d, 0x0b, 0xce, 0xf5, 0xc0, 0xe5, 0x0b, 0x88, 0xa9, 0x55, 0x88, 0x6c,
-      0xfa, 0xea, 0xaa, 0x39, 0x66, 0xdd, 0x80, 0x52, 0xe0, 0x7e, 0x45, 0x8e,
-      0x51, 0x2c, 0x36, 0x07, 0xd7, 0x2b, 0xf1, 0x46, 0x00, 0x66, 0xb2, 0x5a,
-      0x39, 0xbe, 0xf7, 0x26, 0x15, 0xbc, 0x55, 0xdb, 0xe9, 0x01, 0xdd, 0x54,
-      0x27, 0x2b, 0xfe, 0x86, 0x52, 0xef, 0xc6, 0x27, 0xa3, 0xf7, 0x55, 0x55,
-      0xb8, 0xe2, 0x1f, 0xcb, 0x32, 0xd8, 0xba, 0xd6, 0x69, 0xde, 0x8d, 0xa7,
-      0xfa, 0xad, 0xf6, 0x2a, 0xc0, 0x6f, 0x86, 0x50, 0x27, 0x5a, 0xe2, 0xe3,
-      0xf6, 0xb9, 0x01, 0xec, 0x01, 0x37, 0x84, 0x01,
-  };
-  EXPECT_EQ(Bytes(pkcs7_bytes, pkcs7_len),
-            Bytes(kExpectedOutput, sizeof(kExpectedOutput)));
-
-  // Other option combinations should fail.
-  EXPECT_FALSE(
-      PKCS7_sign(cert.get(), key.get(), /*certs=*/nullptr, data_bio.get(),
-                 PKCS7_NOATTR | PKCS7_BINARY | PKCS7_NOCERTS));
-  EXPECT_FALSE(
-      PKCS7_sign(cert.get(), key.get(), /*certs=*/nullptr, data_bio.get(),
-                 PKCS7_BINARY | PKCS7_NOCERTS | PKCS7_DETACHED));
-  EXPECT_FALSE(
-      PKCS7_sign(cert.get(), key.get(), /*certs=*/nullptr, data_bio.get(),
-                 PKCS7_NOATTR | PKCS7_TEXT | PKCS7_NOCERTS | PKCS7_DETACHED));
-  EXPECT_FALSE(
-      PKCS7_sign(cert.get(), key.get(), /*certs=*/nullptr, data_bio.get(),
-                 PKCS7_NOATTR | PKCS7_BINARY | PKCS7_DETACHED));
-
-  ERR_clear_error();
-}
diff --git a/src/crypto/pkcs7/pkcs7_x509.c b/src/crypto/pkcs7/pkcs7_x509.c
index 773c592..3f1526c 100644
--- a/src/crypto/pkcs7/pkcs7_x509.c
+++ b/src/crypto/pkcs7/pkcs7_x509.c
@@ -20,7 +20,6 @@
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
-#include <openssl/obj.h>
 #include <openssl/pem.h>
 #include <openssl/pool.h>
 #include <openssl/stack.h>
@@ -198,9 +197,7 @@
 }
 
 int PKCS7_bundle_certificates(CBB *out, const STACK_OF(X509) *certs) {
-  return pkcs7_add_signed_data(out, /*digest_algos_cb=*/NULL,
-                               pkcs7_bundle_certificates_cb,
-                               /*signer_infos_cb=*/NULL, certs);
+  return pkcs7_bundle(out, pkcs7_bundle_certificates_cb, certs);
 }
 
 static int pkcs7_bundle_crls_cb(CBB *out, const void *arg) {
@@ -231,9 +228,7 @@
 }
 
 int PKCS7_bundle_CRLs(CBB *out, const STACK_OF(X509_CRL) *crls) {
-  return pkcs7_add_signed_data(out, /*digest_algos_cb=*/NULL,
-                               pkcs7_bundle_crls_cb,
-                               /*signer_infos_cb=*/NULL, crls);
+  return pkcs7_bundle(out, pkcs7_bundle_crls_cb, crls);
 }
 
 static PKCS7 *pkcs7_new(CBS *cbs) {
@@ -367,160 +362,26 @@
 int PKCS7_type_is_signed(const PKCS7 *p7) { return 1; }
 int PKCS7_type_is_signedAndEnveloped(const PKCS7 *p7) { return 0; }
 
-// write_sha256_ai writes an AlgorithmIdentifier for SHA-256 to
-// |digest_algos_set|.
-static int write_sha256_ai(CBB *digest_algos_set, const void *arg) {
-  CBB seq;
-  return CBB_add_asn1(digest_algos_set, &seq, CBS_ASN1_SEQUENCE) &&
-         OBJ_nid2cbb(&seq, NID_sha256) &&  //
-         // https://datatracker.ietf.org/doc/html/rfc5754#section-2
-         // "Implementations MUST generate SHA2 AlgorithmIdentifiers with absent
-         //  parameters."
-         CBB_flush(digest_algos_set);
-}
-
-// sign_sha256 writes at most |max_out_sig| bytes of the signature of |data| by
-// |pkey| to |out_sig| and sets |*out_sig_len| to the number of bytes written.
-// It returns one on success or zero on error.
-static int sign_sha256(uint8_t *out_sig, size_t *out_sig_len,
-                       size_t max_out_sig, EVP_PKEY *pkey, BIO *data) {
-  static const size_t kBufSize = 4096;
-  uint8_t *buffer = OPENSSL_malloc(kBufSize);
-  if (!buffer) {
-    return 0;
-  }
-
-  EVP_MD_CTX ctx;
-  EVP_MD_CTX_init(&ctx);
-
-  int ret = 0;
-  if (!EVP_DigestSignInit(&ctx, NULL, EVP_sha256(), NULL, pkey)) {
-    goto out;
-  }
-
-  for (;;) {
-    const int n = BIO_read(data, buffer, kBufSize);
-    if (n == 0) {
-      break;
-    } else if (n < 0 || !EVP_DigestSignUpdate(&ctx, buffer, n)) {
-      goto out;
-    }
-  }
-
-  *out_sig_len = max_out_sig;
-  if (!EVP_DigestSignFinal(&ctx, out_sig, out_sig_len)) {
-    goto out;
-  }
-
-  ret = 1;
-
-out:
-  EVP_MD_CTX_cleanup(&ctx);
-  OPENSSL_free(buffer);
-  return ret;
-}
-
-struct signer_info_data {
-  const X509 *sign_cert;
-  uint8_t *signature;
-  size_t signature_len;
-};
-
-// write_signer_info writes the SignerInfo structure from
-// https://datatracker.ietf.org/doc/html/rfc2315#section-9.2 to |out|. It
-// returns one on success or zero on error.
-static int write_signer_info(CBB *out, const void *arg) {
-  const struct signer_info_data *const si_data = arg;
-
-  int ret = 0;
-  uint8_t *subject_bytes = NULL;
-  uint8_t *serial_bytes = NULL;
-
-  const int subject_len =
-      i2d_X509_NAME(X509_get_subject_name(si_data->sign_cert), &subject_bytes);
-  const int serial_len = i2d_ASN1_INTEGER(
-      (ASN1_INTEGER *)X509_get0_serialNumber(si_data->sign_cert),
-      &serial_bytes);
-
-  CBB seq, issuer_and_serial, signing_algo, null, signature;
-  if (subject_len < 0 ||
-      serial_len < 0 ||
-      !CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
-      // version
-      !CBB_add_asn1_uint64(&seq, 1) ||
-      !CBB_add_asn1(&seq, &issuer_and_serial, CBS_ASN1_SEQUENCE) ||
-      !CBB_add_bytes(&issuer_and_serial, subject_bytes, subject_len) ||
-      !CBB_add_bytes(&issuer_and_serial, serial_bytes, serial_len) ||
-      !write_sha256_ai(&seq, NULL) ||
-      !CBB_add_asn1(&seq, &signing_algo, CBS_ASN1_SEQUENCE) ||
-      !OBJ_nid2cbb(&signing_algo, NID_rsaEncryption) ||
-      !CBB_add_asn1(&signing_algo, &null, CBS_ASN1_NULL) ||
-      !CBB_add_asn1(&seq, &signature, CBS_ASN1_OCTETSTRING) ||
-      !CBB_add_bytes(&signature, si_data->signature, si_data->signature_len) ||
-      !CBB_flush(out)) {
-    goto out;
-  }
-
-  ret = 1;
-
-out:
-  OPENSSL_free(subject_bytes);
-  OPENSSL_free(serial_bytes);
-  return ret;
-}
-
 PKCS7 *PKCS7_sign(X509 *sign_cert, EVP_PKEY *pkey, STACK_OF(X509) *certs,
                   BIO *data, int flags) {
-  CBB cbb;
-  if (!CBB_init(&cbb, 2048)) {
+  if (sign_cert != NULL || pkey != NULL || flags != PKCS7_DETACHED) {
+    OPENSSL_PUT_ERROR(PKCS7, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return NULL;
   }
 
-  uint8_t *der = NULL;
+  uint8_t *der;
   size_t len;
-  PKCS7 *ret = NULL;
-
-  if (sign_cert == NULL && pkey == NULL && flags == PKCS7_DETACHED) {
-    // Caller just wants to bundle certificates.
-    if (!PKCS7_bundle_certificates(&cbb, certs)) {
-      goto out;
-    }
-  } else if (sign_cert != NULL && pkey != NULL && certs == NULL &&
-             data != NULL &&
-             flags == (PKCS7_NOATTR | PKCS7_BINARY | PKCS7_NOCERTS |
-                       PKCS7_DETACHED) &&
-             EVP_PKEY_id(pkey) == NID_rsaEncryption) {
-    // sign-file.c from the Linux kernel.
-    const size_t signature_max_len = EVP_PKEY_size(pkey);
-    struct signer_info_data si_data = {
-      .sign_cert = sign_cert,
-      .signature = OPENSSL_malloc(signature_max_len),
-    };
-
-    if (!si_data.signature ||
-        !sign_sha256(si_data.signature, &si_data.signature_len,
-                     signature_max_len, pkey, data) ||
-        !pkcs7_add_signed_data(&cbb, write_sha256_ai, /*cert_crl_cb=*/NULL,
-                               write_signer_info, &si_data)) {
-      OPENSSL_free(si_data.signature);
-      goto out;
-    }
-    OPENSSL_free(si_data.signature);
-  } else {
-    OPENSSL_PUT_ERROR(PKCS7, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    goto out;
-  }
-
-  if (!CBB_finish(&cbb, &der, &len)) {
-    goto out;
+  CBB cbb;
+  if (!CBB_init(&cbb, 2048) ||
+      !PKCS7_bundle_certificates(&cbb, certs) ||
+      !CBB_finish(&cbb, &der, &len)) {
+    CBB_cleanup(&cbb);
+    return NULL;
   }
 
   CBS cbs;
   CBS_init(&cbs, der, len);
-  ret = pkcs7_new(&cbs);
-
-out:
-  CBB_cleanup(&cbb);
+  PKCS7 *ret = pkcs7_new(&cbs);
   OPENSSL_free(der);
   return ret;
 }
diff --git a/src/crypto/pkcs8/pkcs8_x509.c b/src/crypto/pkcs8/pkcs8_x509.c
index e24fb42..efdf33a 100644
--- a/src/crypto/pkcs8/pkcs8_x509.c
+++ b/src/crypto/pkcs8/pkcs8_x509.c
@@ -1180,7 +1180,7 @@
   }
 
   // PKCS#12 is a very confusing recursive data format, built out of another
-  // recursive data format. Section 5.1 of RFC 7292 describes the encoding
+  // recursive data format. Section 5.1 of RFC7292 describes the encoding
   // algorithm, but there is no clear overview. A quick summary:
   //
   // PKCS#7 defines a ContentInfo structure, which is a overgeneralized typed
diff --git a/src/crypto/test/asm/trampoline-armv4.pl b/src/crypto/test/asm/trampoline-armv4.pl
index d2f5da7..4a61f61 100755
--- a/src/crypto/test/asm/trampoline-armv4.pl
+++ b/src/crypto/test/asm/trampoline-armv4.pl
@@ -178,4 +178,4 @@
 }
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/test/asm/trampoline-armv8.pl b/src/crypto/test/asm/trampoline-armv8.pl
index 76cc025..426360e 100755
--- a/src/crypto/test/asm/trampoline-armv8.pl
+++ b/src/crypto/test/asm/trampoline-armv8.pl
@@ -213,4 +213,4 @@
 }
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/test/asm/trampoline-ppc.pl b/src/crypto/test/asm/trampoline-ppc.pl
index b29c361..a8d7c3f 100755
--- a/src/crypto/test/asm/trampoline-ppc.pl
+++ b/src/crypto/test/asm/trampoline-ppc.pl
@@ -259,4 +259,4 @@
 ____
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/test/asm/trampoline-x86.pl b/src/crypto/test/asm/trampoline-x86.pl
index 9694d16..4244ac2 100755
--- a/src/crypto/test/asm/trampoline-x86.pl
+++ b/src/crypto/test/asm/trampoline-x86.pl
@@ -120,4 +120,4 @@
 
 &asm_finish();
 
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/test/asm/trampoline-x86_64.pl b/src/crypto/test/asm/trampoline-x86_64.pl
index f6d8385..5196141 100755
--- a/src/crypto/test/asm/trampoline-x86_64.pl
+++ b/src/crypto/test/asm/trampoline-x86_64.pl
@@ -556,4 +556,4 @@
 }
 
 print $code;
-close STDOUT or die "error closing STDOUT: $!";
+close STDOUT or die "error closing STDOUT";
diff --git a/src/crypto/x509/a_strex.c b/src/crypto/x509/a_strex.c
new file mode 100644
index 0000000..0c9dcef
--- /dev/null
+++ b/src/crypto/x509/a_strex.c
@@ -0,0 +1,653 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <openssl/asn1.h>
+#include <openssl/mem.h>
+#include <openssl/obj.h>
+
+#include "charmap.h"
+#include "../asn1/internal.h"
+
+/*
+ * ASN1_STRING_print_ex() and X509_NAME_print_ex(). Enhanced string and name
+ * printing routines handling multibyte characters, RFC2253 and a host of
+ * other options.
+ */
+
+#define CHARTYPE_BS_ESC         (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
+
+#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
+                  ASN1_STRFLGS_ESC_QUOTE | \
+                  ASN1_STRFLGS_ESC_CTRL | \
+                  ASN1_STRFLGS_ESC_MSB)
+
+static int send_bio_chars(void *arg, const void *buf, int len)
+{
+    if (!arg)
+        return 1;
+    if (BIO_write(arg, buf, len) != len)
+        return 0;
+    return 1;
+}
+
+static int send_fp_chars(void *arg, const void *buf, int len)
+{
+    if (!arg)
+        return 1;
+    if (fwrite(buf, 1, len, arg) != (unsigned int)len)
+        return 0;
+    return 1;
+}
+
+typedef int char_io (void *arg, const void *buf, int len);
+
+/*
+ * This function handles display of strings, one character at a time. It is
+ * passed an unsigned long for each character because it could come from 2 or
+ * even 4 byte forms.
+ */
+
+#define HEX_SIZE(type) (sizeof(type)*2)
+
+static int do_esc_char(uint32_t c, unsigned char flags, char *do_quotes,
+                       char_io *io_ch, void *arg)
+{
+    unsigned char chflgs, chtmp;
+    char tmphex[HEX_SIZE(uint32_t) + 3];
+
+    if (c > 0xffff) {
+        BIO_snprintf(tmphex, sizeof tmphex, "\\W%08" PRIX32, c);
+        if (!io_ch(arg, tmphex, 10))
+            return -1;
+        return 10;
+    }
+    if (c > 0xff) {
+        BIO_snprintf(tmphex, sizeof tmphex, "\\U%04" PRIX32, c);
+        if (!io_ch(arg, tmphex, 6))
+            return -1;
+        return 6;
+    }
+    chtmp = (unsigned char)c;
+    if (chtmp > 0x7f)
+        chflgs = flags & ASN1_STRFLGS_ESC_MSB;
+    else
+        chflgs = char_type[chtmp] & flags;
+    if (chflgs & CHARTYPE_BS_ESC) {
+        /* If we don't escape with quotes, signal we need quotes */
+        if (chflgs & ASN1_STRFLGS_ESC_QUOTE) {
+            if (do_quotes)
+                *do_quotes = 1;
+            if (!io_ch(arg, &chtmp, 1))
+                return -1;
+            return 1;
+        }
+        if (!io_ch(arg, "\\", 1))
+            return -1;
+        if (!io_ch(arg, &chtmp, 1))
+            return -1;
+        return 2;
+    }
+    if (chflgs & (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB)) {
+        BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
+        if (!io_ch(arg, tmphex, 3))
+            return -1;
+        return 3;
+    }
+    /*
+     * If we get this far and do any escaping at all must escape the escape
+     * character itself: backslash.
+     */
+    if (chtmp == '\\' && flags & ESC_FLAGS) {
+        if (!io_ch(arg, "\\\\", 2))
+            return -1;
+        return 2;
+    }
+    if (!io_ch(arg, &chtmp, 1))
+        return -1;
+    return 1;
+}
+
+#define BUF_TYPE_WIDTH_MASK     0x7
+#define BUF_TYPE_CONVUTF8       0x8
+
+/*
+ * This function sends each character in a buffer to do_esc_char(). It
+ * interprets the content formats and converts to or from UTF8 as
+ * appropriate.
+ */
+
+static int do_buf(unsigned char *buf, int buflen,
+                  int type, unsigned char flags, char *quotes, char_io *io_ch,
+                  void *arg)
+{
+    int i, outlen, len, charwidth;
+    unsigned char orflags, *p, *q;
+    uint32_t c;
+    p = buf;
+    q = buf + buflen;
+    outlen = 0;
+    charwidth = type & BUF_TYPE_WIDTH_MASK;
+
+    switch (charwidth) {
+    case 4:
+        if (buflen & 3) {
+            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_UNIVERSALSTRING);
+            return -1;
+        }
+        break;
+    case 2:
+        if (buflen & 1) {
+            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_BMPSTRING);
+            return -1;
+        }
+        break;
+    default:
+        break;
+    }
+
+    while (p != q) {
+        if (p == buf && flags & ASN1_STRFLGS_ESC_2253)
+            orflags = CHARTYPE_FIRST_ESC_2253;
+        else
+            orflags = 0;
+        switch (charwidth) {
+        case 4:
+            c = ((uint32_t)*p++) << 24;
+            c |= ((uint32_t)*p++) << 16;
+            c |= ((uint32_t)*p++) << 8;
+            c |= *p++;
+            break;
+
+        case 2:
+            c = ((uint32_t)*p++) << 8;
+            c |= *p++;
+            break;
+
+        case 1:
+            c = *p++;
+            break;
+
+        case 0:
+            i = UTF8_getc(p, buflen, &c);
+            if (i < 0)
+                return -1;      /* Invalid UTF8String */
+            buflen -= i;
+            p += i;
+            break;
+        default:
+            return -1;          /* invalid width */
+        }
+        if (p == q && flags & ASN1_STRFLGS_ESC_2253)
+            orflags = CHARTYPE_LAST_ESC_2253;
+        if (type & BUF_TYPE_CONVUTF8) {
+            unsigned char utfbuf[6];
+            int utflen;
+            utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
+            for (i = 0; i < utflen; i++) {
+                /*
+                 * We don't need to worry about setting orflags correctly
+                 * because if utflen==1 its value will be correct anyway
+                 * otherwise each character will be > 0x7f and so the
+                 * character will never be escaped on first and last.
+                 */
+                len =
+                    do_esc_char(utfbuf[i], (unsigned char)(flags | orflags),
+                                quotes, io_ch, arg);
+                if (len < 0)
+                    return -1;
+                outlen += len;
+            }
+        } else {
+            len =
+                do_esc_char(c, (unsigned char)(flags | orflags), quotes,
+                            io_ch, arg);
+            if (len < 0)
+                return -1;
+            outlen += len;
+        }
+    }
+    return outlen;
+}
+
+/* This function hex dumps a buffer of characters */
+
+static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf,
+                       int buflen)
+{
+    static const char hexdig[] = "0123456789ABCDEF";
+    unsigned char *p, *q;
+    char hextmp[2];
+    if (arg) {
+        p = buf;
+        q = buf + buflen;
+        while (p != q) {
+            hextmp[0] = hexdig[*p >> 4];
+            hextmp[1] = hexdig[*p & 0xf];
+            if (!io_ch(arg, hextmp, 2))
+                return -1;
+            p++;
+        }
+    }
+    return buflen << 1;
+}
+
+/*
+ * "dump" a string. This is done when the type is unknown, or the flags
+ * request it. We can either dump the content octets or the entire DER
+ * encoding. This uses the RFC2253 #01234 format.
+ */
+
+static int do_dump(unsigned long lflags, char_io *io_ch, void *arg,
+                   const ASN1_STRING *str)
+{
+    /*
+     * Placing the ASN1_STRING in a temp ASN1_TYPE allows the DER encoding to
+     * readily obtained
+     */
+    ASN1_TYPE t;
+    unsigned char *der_buf, *p;
+    int outlen, der_len;
+
+    if (!io_ch(arg, "#", 1))
+        return -1;
+    /* If we don't dump DER encoding just dump content octets */
+    if (!(lflags & ASN1_STRFLGS_DUMP_DER)) {
+        outlen = do_hex_dump(io_ch, arg, str->data, str->length);
+        if (outlen < 0)
+            return -1;
+        return outlen + 1;
+    }
+    t.type = str->type;
+    t.value.ptr = (char *)str;
+    der_len = i2d_ASN1_TYPE(&t, NULL);
+    der_buf = OPENSSL_malloc(der_len);
+    if (!der_buf)
+        return -1;
+    p = der_buf;
+    i2d_ASN1_TYPE(&t, &p);
+    outlen = do_hex_dump(io_ch, arg, der_buf, der_len);
+    OPENSSL_free(der_buf);
+    if (outlen < 0)
+        return -1;
+    return outlen + 1;
+}
+
+/*
+ * Lookup table to convert tags to character widths, 0 = UTF8 encoded, -1 is
+ * used for non string types otherwise it is the number of bytes per
+ * character
+ */
+
+static const signed char tag2nbyte[] = {
+    -1, -1, -1, -1, -1,         /* 0-4 */
+    -1, -1, -1, -1, -1,         /* 5-9 */
+    -1, -1, 0, -1,              /* 10-13 */
+    -1, -1, -1, -1,             /* 15-17 */
+    1, 1, 1,                    /* 18-20 */
+    -1, 1, 1, 1,                /* 21-24 */
+    -1, 1, -1,                  /* 25-27 */
+    4, -1, 2                    /* 28-30 */
+};
+
+/*
+ * This is the main function, print out an ASN1_STRING taking note of various
+ * escape and display options. Returns number of characters written or -1 if
+ * an error occurred.
+ */
+
+static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags,
+                       const ASN1_STRING *str)
+{
+    int outlen, len;
+    int type;
+    char quotes;
+    unsigned char flags;
+    quotes = 0;
+    /* Keep a copy of escape flags */
+    flags = (unsigned char)(lflags & ESC_FLAGS);
+
+    type = str->type;
+
+    outlen = 0;
+
+    if (lflags & ASN1_STRFLGS_SHOW_TYPE) {
+        const char *tagname;
+        tagname = ASN1_tag2str(type);
+        outlen += strlen(tagname);
+        if (!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1))
+            return -1;
+        outlen++;
+    }
+
+    /* Decide what to do with type, either dump content or display it */
+
+    /* Dump everything */
+    if (lflags & ASN1_STRFLGS_DUMP_ALL)
+        type = -1;
+    /* Ignore the string type */
+    else if (lflags & ASN1_STRFLGS_IGNORE_TYPE)
+        type = 1;
+    else {
+        /* Else determine width based on type */
+        if ((type > 0) && (type < 31))
+            type = tag2nbyte[type];
+        else
+            type = -1;
+        if ((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN))
+            type = 1;
+    }
+
+    if (type == -1) {
+        len = do_dump(lflags, io_ch, arg, str);
+        if (len < 0)
+            return -1;
+        outlen += len;
+        return outlen;
+    }
+
+    if (lflags & ASN1_STRFLGS_UTF8_CONVERT) {
+        /*
+         * Note: if string is UTF8 and we want to convert to UTF8 then we
+         * just interpret it as 1 byte per character to avoid converting
+         * twice.
+         */
+        if (!type)
+            type = 1;
+        else
+            type |= BUF_TYPE_CONVUTF8;
+    }
+
+    len = do_buf(str->data, str->length, type, flags, &quotes, io_ch, NULL);
+    if (len < 0)
+        return -1;
+    outlen += len;
+    if (quotes)
+        outlen += 2;
+    if (!arg)
+        return outlen;
+    if (quotes && !io_ch(arg, "\"", 1))
+        return -1;
+    if (do_buf(str->data, str->length, type, flags, NULL, io_ch, arg) < 0)
+        return -1;
+    if (quotes && !io_ch(arg, "\"", 1))
+        return -1;
+    return outlen;
+}
+
+/* Used for line indenting: print 'indent' spaces */
+
+static int do_indent(char_io *io_ch, void *arg, int indent)
+{
+    int i;
+    for (i = 0; i < indent; i++)
+        if (!io_ch(arg, " ", 1))
+            return 0;
+    return 1;
+}
+
+#define FN_WIDTH_LN     25
+#define FN_WIDTH_SN     10
+
+static int do_name_ex(char_io *io_ch, void *arg, const X509_NAME *n,
+                      int indent, unsigned long flags)
+{
+    int i, prev = -1, orflags, cnt;
+    int fn_opt, fn_nid;
+    ASN1_OBJECT *fn;
+    ASN1_STRING *val;
+    X509_NAME_ENTRY *ent;
+    char objtmp[80];
+    const char *objbuf;
+    int outlen, len;
+    const char *sep_dn, *sep_mv, *sep_eq;
+    int sep_dn_len, sep_mv_len, sep_eq_len;
+    if (indent < 0)
+        indent = 0;
+    outlen = indent;
+    if (!do_indent(io_ch, arg, indent))
+        return -1;
+    switch (flags & XN_FLAG_SEP_MASK) {
+    case XN_FLAG_SEP_MULTILINE:
+        sep_dn = "\n";
+        sep_dn_len = 1;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        break;
+
+    case XN_FLAG_SEP_COMMA_PLUS:
+        sep_dn = ",";
+        sep_dn_len = 1;
+        sep_mv = "+";
+        sep_mv_len = 1;
+        indent = 0;
+        break;
+
+    case XN_FLAG_SEP_CPLUS_SPC:
+        sep_dn = ", ";
+        sep_dn_len = 2;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        indent = 0;
+        break;
+
+    case XN_FLAG_SEP_SPLUS_SPC:
+        sep_dn = "; ";
+        sep_dn_len = 2;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        indent = 0;
+        break;
+
+    default:
+        return -1;
+    }
+
+    if (flags & XN_FLAG_SPC_EQ) {
+        sep_eq = " = ";
+        sep_eq_len = 3;
+    } else {
+        sep_eq = "=";
+        sep_eq_len = 1;
+    }
+
+    fn_opt = flags & XN_FLAG_FN_MASK;
+
+    cnt = X509_NAME_entry_count(n);
+    for (i = 0; i < cnt; i++) {
+        if (flags & XN_FLAG_DN_REV)
+            ent = X509_NAME_get_entry(n, cnt - i - 1);
+        else
+            ent = X509_NAME_get_entry(n, i);
+        if (prev != -1) {
+            if (prev == ent->set) {
+                if (!io_ch(arg, sep_mv, sep_mv_len))
+                    return -1;
+                outlen += sep_mv_len;
+            } else {
+                if (!io_ch(arg, sep_dn, sep_dn_len))
+                    return -1;
+                outlen += sep_dn_len;
+                if (!do_indent(io_ch, arg, indent))
+                    return -1;
+                outlen += indent;
+            }
+        }
+        prev = ent->set;
+        fn = X509_NAME_ENTRY_get_object(ent);
+        val = X509_NAME_ENTRY_get_data(ent);
+        fn_nid = OBJ_obj2nid(fn);
+        if (fn_opt != XN_FLAG_FN_NONE) {
+            int objlen, fld_len;
+            if ((fn_opt == XN_FLAG_FN_OID) || (fn_nid == NID_undef)) {
+                OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1);
+                fld_len = 0;    /* XXX: what should this be? */
+                objbuf = objtmp;
+            } else {
+                if (fn_opt == XN_FLAG_FN_SN) {
+                    fld_len = FN_WIDTH_SN;
+                    objbuf = OBJ_nid2sn(fn_nid);
+                } else if (fn_opt == XN_FLAG_FN_LN) {
+                    fld_len = FN_WIDTH_LN;
+                    objbuf = OBJ_nid2ln(fn_nid);
+                } else {
+                    fld_len = 0; /* XXX: what should this be? */
+                    objbuf = "";
+                }
+            }
+            objlen = strlen(objbuf);
+            if (!io_ch(arg, objbuf, objlen))
+                return -1;
+            if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) {
+                if (!do_indent(io_ch, arg, fld_len - objlen))
+                    return -1;
+                outlen += fld_len - objlen;
+            }
+            if (!io_ch(arg, sep_eq, sep_eq_len))
+                return -1;
+            outlen += objlen + sep_eq_len;
+        }
+        /*
+         * If the field name is unknown then fix up the DER dump flag. We
+         * might want to limit this further so it will DER dump on anything
+         * other than a few 'standard' fields.
+         */
+        if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS))
+            orflags = ASN1_STRFLGS_DUMP_ALL;
+        else
+            orflags = 0;
+
+        len = do_print_ex(io_ch, arg, flags | orflags, val);
+        if (len < 0)
+            return -1;
+        outlen += len;
+    }
+    return outlen;
+}
+
+/* Wrappers round the main functions */
+
+int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent,
+                       unsigned long flags)
+{
+    if (flags == XN_FLAG_COMPAT)
+        return X509_NAME_print(out, nm, indent);
+    return do_name_ex(send_bio_chars, out, nm, indent, flags);
+}
+
+#ifndef OPENSSL_NO_FP_API
+int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, int indent,
+                          unsigned long flags)
+{
+    if (flags == XN_FLAG_COMPAT) {
+        BIO *btmp;
+        int ret;
+        btmp = BIO_new_fp(fp, BIO_NOCLOSE);
+        if (!btmp)
+            return -1;
+        ret = X509_NAME_print(btmp, nm, indent);
+        BIO_free(btmp);
+        return ret;
+    }
+    return do_name_ex(send_fp_chars, fp, nm, indent, flags);
+}
+#endif
+
+int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long flags)
+{
+    return do_print_ex(send_bio_chars, out, flags, str);
+}
+
+#ifndef OPENSSL_NO_FP_API
+int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str, unsigned long flags)
+{
+    return do_print_ex(send_fp_chars, fp, flags, str);
+}
+#endif
+
+/*
+ * Utility function: convert any string type to UTF8, returns number of bytes
+ * in output string or a negative error code
+ */
+
+int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in)
+{
+    ASN1_STRING stmp, *str = &stmp;
+    int mbflag, type, ret;
+    if (!in)
+        return -1;
+    type = in->type;
+    if ((type < 0) || (type > 30))
+        return -1;
+    mbflag = tag2nbyte[type];
+    if (mbflag == -1)
+        return -1;
+    mbflag |= MBSTRING_FLAG;
+    stmp.data = NULL;
+    stmp.length = 0;
+    stmp.flags = 0;
+    ret =
+        ASN1_mbstring_copy(&str, in->data, in->length, mbflag,
+                           B_ASN1_UTF8STRING);
+    if (ret < 0)
+        return ret;
+    *out = stmp.data;
+    return stmp.length;
+}
diff --git a/src/crypto/x509/by_file.c b/src/crypto/x509/by_file.c
index 1614c8c..994beb9 100644
--- a/src/crypto/x509/by_file.c
+++ b/src/crypto/x509/by_file.c
@@ -61,8 +61,6 @@
 #include <openssl/pem.h>
 #include <openssl/thread.h>
 
-#include "internal.h"
-
 #ifndef OPENSSL_NO_STDIO
 
 static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
diff --git a/src/crypto/asn1/charmap.h b/src/crypto/x509/charmap.h
similarity index 100%
rename from src/crypto/asn1/charmap.h
rename to src/crypto/x509/charmap.h
diff --git a/src/crypto/x509/internal.h b/src/crypto/x509/internal.h
index ac68755..2782f23 100644
--- a/src/crypto/x509/internal.h
+++ b/src/crypto/x509/internal.h
@@ -81,22 +81,6 @@
   EVP_PKEY *pkey;
 } /* X509_PUBKEY */;
 
-struct X509_name_entry_st {
-  ASN1_OBJECT *object;
-  ASN1_STRING *value;
-  int set;
-} /* X509_NAME_ENTRY */;
-
-// we always keep X509_NAMEs in 2 forms.
-struct X509_name_st {
-  STACK_OF(X509_NAME_ENTRY) *entries;
-  int modified;  // true if 'bytes' needs to be built
-  BUF_MEM *bytes;
-  // unsigned long hash; Keep the hash around for lookups
-  unsigned char *canon_enc;
-  int canon_enclen;
-} /* X509_NAME */;
-
 struct x509_attributes_st {
   ASN1_OBJECT *object;
   STACK_OF(ASN1_TYPE) *set;
@@ -117,47 +101,6 @@
 } /* X509_EXTENSION */;
 
 typedef struct {
-  ASN1_INTEGER *version;  // [ 0 ] default of v1
-  ASN1_INTEGER *serialNumber;
-  X509_ALGOR *signature;
-  X509_NAME *issuer;
-  X509_VAL *validity;
-  X509_NAME *subject;
-  X509_PUBKEY *key;
-  ASN1_BIT_STRING *issuerUID;            // [ 1 ] optional in v2
-  ASN1_BIT_STRING *subjectUID;           // [ 2 ] optional in v2
-  STACK_OF(X509_EXTENSION) *extensions;  // [ 3 ] optional in v3
-  ASN1_ENCODING enc;
-} X509_CINF;
-
-DECLARE_ASN1_FUNCTIONS(X509_CINF)
-
-struct x509_st {
-  X509_CINF *cert_info;
-  X509_ALGOR *sig_alg;
-  ASN1_BIT_STRING *signature;
-  CRYPTO_refcount_t references;
-  CRYPTO_EX_DATA ex_data;
-  // These contain copies of various extension values
-  long ex_pathlen;
-  long ex_pcpathlen;
-  unsigned long ex_flags;
-  unsigned long ex_kusage;
-  unsigned long ex_xkusage;
-  unsigned long ex_nscert;
-  ASN1_OCTET_STRING *skid;
-  AUTHORITY_KEYID *akid;
-  X509_POLICY_CACHE *policy_cache;
-  STACK_OF(DIST_POINT) *crldp;
-  STACK_OF(GENERAL_NAME) *altname;
-  NAME_CONSTRAINTS *nc;
-  unsigned char sha1_hash[SHA_DIGEST_LENGTH];
-  X509_CERT_AUX *aux;
-  CRYPTO_BUFFER *buf;
-  CRYPTO_MUTEX lock;
-} /* X509 */;
-
-typedef struct {
   ASN1_ENCODING enc;
   ASN1_INTEGER *version;
   X509_NAME *subject;
@@ -210,6 +153,7 @@
   void *meth_data;
 } /* X509_CRL */;
 
+
 struct X509_VERIFY_PARAM_st {
   char *name;
   time_t check_time;                // Time to use
@@ -230,131 +174,6 @@
   unsigned char poison;  // Fail all verifications at name checking
 } /* X509_VERIFY_PARAM */;
 
-struct x509_object_st {
-  // one of the above types
-  int type;
-  union {
-    char *ptr;
-    X509 *x509;
-    X509_CRL *crl;
-    EVP_PKEY *pkey;
-  } data;
-} /* X509_OBJECT */;
-
-// This is a static that defines the function interface
-struct x509_lookup_method_st {
-  const char *name;
-  int (*new_item)(X509_LOOKUP *ctx);
-  void (*free)(X509_LOOKUP *ctx);
-  int (*init)(X509_LOOKUP *ctx);
-  int (*shutdown)(X509_LOOKUP *ctx);
-  int (*ctrl)(X509_LOOKUP *ctx, int cmd, const char *argc, long argl,
-              char **ret);
-  int (*get_by_subject)(X509_LOOKUP *ctx, int type, X509_NAME *name,
-                        X509_OBJECT *ret);
-  int (*get_by_issuer_serial)(X509_LOOKUP *ctx, int type, X509_NAME *name,
-                              ASN1_INTEGER *serial, X509_OBJECT *ret);
-  int (*get_by_fingerprint)(X509_LOOKUP *ctx, int type, unsigned char *bytes,
-                            int len, X509_OBJECT *ret);
-  int (*get_by_alias)(X509_LOOKUP *ctx, int type, char *str, int len,
-                      X509_OBJECT *ret);
-} /* X509_LOOKUP_METHOD */;
-
-// This is used to hold everything.  It is used for all certificate
-// validation.  Once we have a certificate chain, the 'verify'
-// function is then called to actually check the cert chain.
-struct x509_store_st {
-  // The following is a cache of trusted certs
-  int cache;                    // if true, stash any hits
-  STACK_OF(X509_OBJECT) *objs;  // Cache of all objects
-  CRYPTO_MUTEX objs_lock;
-  STACK_OF(X509) *additional_untrusted;
-
-  // These are external lookup methods
-  STACK_OF(X509_LOOKUP) *get_cert_methods;
-
-  X509_VERIFY_PARAM *param;
-
-  // Callbacks for various operations
-  X509_STORE_CTX_verify_fn verify;          // called to verify a certificate
-  X509_STORE_CTX_verify_cb verify_cb;       // error callback
-  X509_STORE_CTX_get_issuer_fn get_issuer;  // get issuers cert from ctx
-  X509_STORE_CTX_check_issued_fn check_issued;  // check issued
-  X509_STORE_CTX_check_revocation_fn
-      check_revocation;                   // Check revocation status of chain
-  X509_STORE_CTX_get_crl_fn get_crl;      // retrieve CRL
-  X509_STORE_CTX_check_crl_fn check_crl;  // Check CRL validity
-  X509_STORE_CTX_cert_crl_fn cert_crl;    // Check certificate against CRL
-  X509_STORE_CTX_lookup_certs_fn lookup_certs;
-  X509_STORE_CTX_lookup_crls_fn lookup_crls;
-  X509_STORE_CTX_cleanup_fn cleanup;
-
-  CRYPTO_refcount_t references;
-} /* X509_STORE */;
-
-
-// This is the functions plus an instance of the local variables.
-struct x509_lookup_st {
-  int init;                    // have we been started
-  int skip;                    // don't use us.
-  X509_LOOKUP_METHOD *method;  // the functions
-  char *method_data;           // method data
-
-  X509_STORE *store_ctx;  // who owns us
-} /* X509_LOOKUP */;
-
-// This is a used when verifying cert chains.  Since the
-// gathering of the cert chain can take some time (and have to be
-// 'retried', this needs to be kept and passed around.
-struct x509_store_ctx_st {
-  X509_STORE *ctx;
-
-  // The following are set by the caller
-  X509 *cert;                 // The cert to check
-  STACK_OF(X509) *untrusted;  // chain of X509s - untrusted - passed in
-  STACK_OF(X509_CRL) *crls;   // set of CRLs passed in
-
-  X509_VERIFY_PARAM *param;
-  void *other_ctx;  // Other info for use with get_issuer()
-
-  // Callbacks for various operations
-  X509_STORE_CTX_verify_fn verify;          // called to verify a certificate
-  X509_STORE_CTX_verify_cb verify_cb;       // error callback
-  X509_STORE_CTX_get_issuer_fn get_issuer;  // get issuers cert from ctx
-  X509_STORE_CTX_check_issued_fn check_issued;  // check issued
-  X509_STORE_CTX_check_revocation_fn
-      check_revocation;                   // Check revocation status of chain
-  X509_STORE_CTX_get_crl_fn get_crl;      // retrieve CRL
-  X509_STORE_CTX_check_crl_fn check_crl;  // Check CRL validity
-  X509_STORE_CTX_cert_crl_fn cert_crl;    // Check certificate against CRL
-  X509_STORE_CTX_check_policy_fn check_policy;
-  X509_STORE_CTX_lookup_certs_fn lookup_certs;
-  X509_STORE_CTX_lookup_crls_fn lookup_crls;
-  X509_STORE_CTX_cleanup_fn cleanup;
-
-  // The following is built up
-  int valid;               // if 0, rebuild chain
-  int last_untrusted;      // index of last untrusted cert
-  STACK_OF(X509) *chain;   // chain of X509s - built up and trusted
-  X509_POLICY_TREE *tree;  // Valid policy tree
-
-  int explicit_policy;  // Require explicit policy value
-
-  // When something goes wrong, this is why
-  int error_depth;
-  int error;
-  X509 *current_cert;
-  X509 *current_issuer;   // cert currently being tested as valid issuer
-  X509_CRL *current_crl;  // current CRL
-
-  int current_crl_score;         // score of current CRL
-  unsigned int current_reasons;  // Reason mask
-
-  X509_STORE_CTX *parent;  // For CRL path validation: parent context
-
-  CRYPTO_EX_DATA ex_data;
-} /* X509_STORE_CTX */;
-
 
 /* RSA-PSS functions. */
 
diff --git a/src/crypto/x509/name_print.c b/src/crypto/x509/name_print.c
deleted file mode 100644
index b5523c0..0000000
--- a/src/crypto/x509/name_print.c
+++ /dev/null
@@ -1,246 +0,0 @@
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.] */
-
-#include <openssl/x509.h>
-
-#include <inttypes.h>
-#include <string.h>
-
-#include <openssl/asn1.h>
-#include <openssl/bio.h>
-#include <openssl/obj.h>
-
-
-static int maybe_write(BIO *out, const void *buf, int len)
-{
-    /* If |out| is NULL, ignore the output but report the length. */
-    return out == NULL || BIO_write(out, buf, len) == len;
-}
-
-/* do_indent prints |indent| spaces to |out|. */
-static int do_indent(BIO *out, int indent)
-{
-    for (int i = 0; i < indent; i++) {
-        if (!maybe_write(out, " ", 1)) {
-            return 0;
-        }
-    }
-    return 1;
-}
-
-#define FN_WIDTH_LN     25
-#define FN_WIDTH_SN     10
-
-static int do_name_ex(BIO *out, const X509_NAME *n, int indent,
-                      unsigned long flags)
-{
-    int i, prev = -1, orflags, cnt;
-    int fn_opt, fn_nid;
-    ASN1_OBJECT *fn;
-    ASN1_STRING *val;
-    X509_NAME_ENTRY *ent;
-    char objtmp[80];
-    const char *objbuf;
-    int outlen, len;
-    const char *sep_dn, *sep_mv, *sep_eq;
-    int sep_dn_len, sep_mv_len, sep_eq_len;
-    if (indent < 0)
-        indent = 0;
-    outlen = indent;
-    if (!do_indent(out, indent))
-        return -1;
-    switch (flags & XN_FLAG_SEP_MASK) {
-    case XN_FLAG_SEP_MULTILINE:
-        sep_dn = "\n";
-        sep_dn_len = 1;
-        sep_mv = " + ";
-        sep_mv_len = 3;
-        break;
-
-    case XN_FLAG_SEP_COMMA_PLUS:
-        sep_dn = ",";
-        sep_dn_len = 1;
-        sep_mv = "+";
-        sep_mv_len = 1;
-        indent = 0;
-        break;
-
-    case XN_FLAG_SEP_CPLUS_SPC:
-        sep_dn = ", ";
-        sep_dn_len = 2;
-        sep_mv = " + ";
-        sep_mv_len = 3;
-        indent = 0;
-        break;
-
-    case XN_FLAG_SEP_SPLUS_SPC:
-        sep_dn = "; ";
-        sep_dn_len = 2;
-        sep_mv = " + ";
-        sep_mv_len = 3;
-        indent = 0;
-        break;
-
-    default:
-        return -1;
-    }
-
-    if (flags & XN_FLAG_SPC_EQ) {
-        sep_eq = " = ";
-        sep_eq_len = 3;
-    } else {
-        sep_eq = "=";
-        sep_eq_len = 1;
-    }
-
-    fn_opt = flags & XN_FLAG_FN_MASK;
-
-    cnt = X509_NAME_entry_count(n);
-    for (i = 0; i < cnt; i++) {
-        if (flags & XN_FLAG_DN_REV)
-            ent = X509_NAME_get_entry(n, cnt - i - 1);
-        else
-            ent = X509_NAME_get_entry(n, i);
-        if (prev != -1) {
-            if (prev == X509_NAME_ENTRY_set(ent)) {
-                if (!maybe_write(out, sep_mv, sep_mv_len))
-                    return -1;
-                outlen += sep_mv_len;
-            } else {
-                if (!maybe_write(out, sep_dn, sep_dn_len))
-                    return -1;
-                outlen += sep_dn_len;
-                if (!do_indent(out, indent))
-                    return -1;
-                outlen += indent;
-            }
-        }
-        prev = X509_NAME_ENTRY_set(ent);
-        fn = X509_NAME_ENTRY_get_object(ent);
-        val = X509_NAME_ENTRY_get_data(ent);
-        fn_nid = OBJ_obj2nid(fn);
-        if (fn_opt != XN_FLAG_FN_NONE) {
-            int objlen, fld_len;
-            if ((fn_opt == XN_FLAG_FN_OID) || (fn_nid == NID_undef)) {
-                OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1);
-                fld_len = 0;    /* XXX: what should this be? */
-                objbuf = objtmp;
-            } else {
-                if (fn_opt == XN_FLAG_FN_SN) {
-                    fld_len = FN_WIDTH_SN;
-                    objbuf = OBJ_nid2sn(fn_nid);
-                } else if (fn_opt == XN_FLAG_FN_LN) {
-                    fld_len = FN_WIDTH_LN;
-                    objbuf = OBJ_nid2ln(fn_nid);
-                } else {
-                    fld_len = 0; /* XXX: what should this be? */
-                    objbuf = "";
-                }
-            }
-            objlen = strlen(objbuf);
-            if (!maybe_write(out, objbuf, objlen))
-                return -1;
-            if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) {
-                if (!do_indent(out, fld_len - objlen))
-                    return -1;
-                outlen += fld_len - objlen;
-            }
-            if (!maybe_write(out, sep_eq, sep_eq_len))
-                return -1;
-            outlen += objlen + sep_eq_len;
-        }
-        /*
-         * If the field name is unknown then fix up the DER dump flag. We
-         * might want to limit this further so it will DER dump on anything
-         * other than a few 'standard' fields.
-         */
-        if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS))
-            orflags = ASN1_STRFLGS_DUMP_ALL;
-        else
-            orflags = 0;
-
-        len = ASN1_STRING_print_ex(out, val, flags | orflags);
-        if (len < 0)
-            return -1;
-        outlen += len;
-    }
-    return outlen;
-}
-
-int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent,
-                       unsigned long flags)
-{
-    if (flags == XN_FLAG_COMPAT)
-        return X509_NAME_print(out, nm, indent);
-    return do_name_ex(out, nm, indent, flags);
-}
-
-int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, int indent,
-                          unsigned long flags)
-{
-    BIO *bio = NULL;
-    if (fp != NULL) {
-        /* If |fp| is NULL, this function returns the number of bytes without
-         * writing. */
-        bio = BIO_new_fp(fp, BIO_NOCLOSE);
-        if (bio == NULL) {
-            return -1;
-        }
-    }
-    int ret = X509_NAME_print_ex(bio, nm, indent, flags);
-    BIO_free(bio);
-    return ret;
-}
diff --git a/src/crypto/x509/rsa_pss.c b/src/crypto/x509/rsa_pss.c
index 21a6bea..1520c08 100644
--- a/src/crypto/x509/rsa_pss.c
+++ b/src/crypto/x509/rsa_pss.c
@@ -67,21 +67,12 @@
 #include "internal.h"
 
 
-static int rsa_pss_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
-                      void *exarg) {
-  if (operation == ASN1_OP_FREE_PRE) {
-    RSA_PSS_PARAMS *pss = (RSA_PSS_PARAMS *)*pval;
-    X509_ALGOR_free(pss->maskHash);
-  }
-  return 1;
-}
-
-ASN1_SEQUENCE_cb(RSA_PSS_PARAMS, rsa_pss_cb) = {
+ASN1_SEQUENCE(RSA_PSS_PARAMS) = {
   ASN1_EXP_OPT(RSA_PSS_PARAMS, hashAlgorithm, X509_ALGOR,0),
   ASN1_EXP_OPT(RSA_PSS_PARAMS, maskGenAlgorithm, X509_ALGOR,1),
   ASN1_EXP_OPT(RSA_PSS_PARAMS, saltLength, ASN1_INTEGER,2),
   ASN1_EXP_OPT(RSA_PSS_PARAMS, trailerField, ASN1_INTEGER,3),
-} ASN1_SEQUENCE_END_cb(RSA_PSS_PARAMS, RSA_PSS_PARAMS)
+} ASN1_SEQUENCE_END(RSA_PSS_PARAMS)
 
 IMPLEMENT_ASN1_FUNCTIONS(RSA_PSS_PARAMS)
 
diff --git a/src/crypto/x509/t_crl.c b/src/crypto/x509/t_crl.c
index d924f85..42f05cd 100644
--- a/src/crypto/x509/t_crl.c
+++ b/src/crypto/x509/t_crl.c
@@ -61,6 +61,7 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
+#ifndef OPENSSL_NO_FP_API
 int X509_CRL_print_fp(FILE *fp, X509_CRL *x)
 {
     BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
@@ -72,6 +73,7 @@
     BIO_free(b);
     return ret;
 }
+#endif
 
 int X509_CRL_print(BIO *out, X509_CRL *x)
 {
diff --git a/src/crypto/x509/t_x509.c b/src/crypto/x509/t_x509.c
index 7c32a87..5db8746 100644
--- a/src/crypto/x509/t_x509.c
+++ b/src/crypto/x509/t_x509.c
@@ -54,6 +54,7 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
+#include <ctype.h>
 #include <openssl/asn1.h>
 #include <openssl/bio.h>
 #include <openssl/digest.h>
@@ -67,6 +68,7 @@
 #include "internal.h"
 
 
+#ifndef OPENSSL_NO_FP_API
 int X509_print_ex_fp(FILE *fp, X509 *x, unsigned long nmflag,
                      unsigned long cflag)
 {
@@ -84,6 +86,7 @@
 {
     return X509_print_ex_fp(fp, x, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
 }
+#endif
 
 int X509_print(BIO *bp, X509 *x)
 {
@@ -315,6 +318,182 @@
     return 1;
 }
 
+int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
+{
+    int i, n;
+    char buf[80];
+    const char *p;
+
+    if (v == NULL)
+        return (0);
+    n = 0;
+    p = (const char *)v->data;
+    for (i = 0; i < v->length; i++) {
+        if ((p[i] > '~') || ((p[i] < ' ') &&
+                             (p[i] != '\n') && (p[i] != '\r')))
+            buf[n] = '.';
+        else
+            buf[n] = p[i];
+        n++;
+        if (n >= 80) {
+            if (BIO_write(bp, buf, n) <= 0)
+                return (0);
+            n = 0;
+        }
+    }
+    if (n > 0)
+        if (BIO_write(bp, buf, n) <= 0)
+            return (0);
+    return (1);
+}
+
+int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
+{
+    if (tm->type == V_ASN1_UTCTIME)
+        return ASN1_UTCTIME_print(bp, tm);
+    if (tm->type == V_ASN1_GENERALIZEDTIME)
+        return ASN1_GENERALIZEDTIME_print(bp, tm);
+    BIO_write(bp, "Bad time value", 14);
+    return (0);
+}
+
+static const char *const mon[12] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
+{
+    char *v;
+    int gmt = 0;
+    int i;
+    int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
+    char *f = NULL;
+    int f_len = 0;
+
+    i = tm->length;
+    v = (char *)tm->data;
+
+    if (i < 12)
+        goto err;
+    if (v[i - 1] == 'Z')
+        gmt = 1;
+    for (i = 0; i < 12; i++)
+        if ((v[i] > '9') || (v[i] < '0'))
+            goto err;
+    y = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] -
+                                                                        '0');
+    M = (v[4] - '0') * 10 + (v[5] - '0');
+    if ((M > 12) || (M < 1))
+        goto err;
+    d = (v[6] - '0') * 10 + (v[7] - '0');
+    h = (v[8] - '0') * 10 + (v[9] - '0');
+    m = (v[10] - '0') * 10 + (v[11] - '0');
+    if (tm->length >= 14 &&
+        (v[12] >= '0') && (v[12] <= '9') &&
+        (v[13] >= '0') && (v[13] <= '9')) {
+        s = (v[12] - '0') * 10 + (v[13] - '0');
+        /* Check for fractions of seconds. */
+        if (tm->length >= 15 && v[14] == '.') {
+            int l = tm->length;
+            f = &v[14];         /* The decimal point. */
+            f_len = 1;
+            while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
+                ++f_len;
+        }
+    }
+
+    if (BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
+                   mon[M - 1], d, h, m, s, f_len, f, y,
+                   (gmt) ? " GMT" : "") <= 0)
+        return (0);
+    else
+        return (1);
+ err:
+    BIO_write(bp, "Bad time value", 14);
+    return (0);
+}
+
+// consume_two_digits is a helper function for ASN1_UTCTIME_print. If |*v|,
+// assumed to be |*len| bytes long, has two leading digits, updates |*out| with
+// their value, updates |v| and |len|, and returns one. Otherwise, returns
+// zero.
+static int consume_two_digits(int* out, const char **v, int *len) {
+  if (*len < 2|| !isdigit((*v)[0]) || !isdigit((*v)[1])) {
+    return 0;
+  }
+  *out = ((*v)[0] - '0') * 10 + ((*v)[1] - '0');
+  *len -= 2;
+  *v += 2;
+  return 1;
+}
+
+// consume_zulu_timezone is a helper function for ASN1_UTCTIME_print. If |*v|,
+// assumed to be |*len| bytes long, starts with "Z" then it updates |*v| and
+// |*len| and returns one. Otherwise returns zero.
+static int consume_zulu_timezone(const char **v, int *len) {
+  if (*len == 0 || (*v)[0] != 'Z') {
+    return 0;
+  }
+
+  *len -= 1;
+  *v += 1;
+  return 1;
+}
+
+int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm) {
+  const char *v = (const char *)tm->data;
+  int len = tm->length;
+  int Y = 0, M = 0, D = 0, h = 0, m = 0, s = 0;
+
+  // YYMMDDhhmm are required to be present.
+  if (!consume_two_digits(&Y, &v, &len) ||
+      !consume_two_digits(&M, &v, &len) ||
+      !consume_two_digits(&D, &v, &len) ||
+      !consume_two_digits(&h, &v, &len) ||
+      !consume_two_digits(&m, &v, &len)) {
+    goto err;
+  }
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires seconds
+  // to be present, but historically this code has forgiven its absence.
+  consume_two_digits(&s, &v, &len);
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, specifies this
+  // interpretation of the year.
+  if (Y < 50) {
+    Y += 2000;
+  } else {
+    Y += 1900;
+  }
+  if (M > 12 || M == 0) {
+    goto err;
+  }
+  if (D > 31 || D == 0) {
+    goto err;
+  }
+  if (h > 23 || m > 59 || s > 60) {
+    goto err;
+  }
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires the "Z"
+  // to be present, but historically this code has forgiven its absence.
+  const int is_gmt = consume_zulu_timezone(&v, &len);
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, does not permit
+  // the specification of timezones using the +hhmm / -hhmm syntax, which is
+  // the only other thing that might legitimately be found at the end.
+  if (len) {
+    goto err;
+  }
+
+  return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s", mon[M - 1], D, h, m, s, Y,
+                    is_gmt ? " GMT" : "") > 0;
+
+err:
+  BIO_write(bp, "Bad time value", 14);
+  return 0;
+}
+
 int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase)
 {
     char *s, *c, *b;
diff --git a/src/crypto/x509/t_x509a.c b/src/crypto/x509/t_x509a.c
index 4c7b212..7fbb47b 100644
--- a/src/crypto/x509/t_x509a.c
+++ b/src/crypto/x509/t_x509a.c
@@ -102,10 +102,8 @@
         BIO_puts(out, "\n");
     } else
         BIO_printf(out, "%*sNo Rejected Uses.\n", indent, "");
-    if (aux->alias) {
-        BIO_printf(out, "%*sAlias: %.*s\n", indent, "", aux->alias->length,
-                   aux->alias->data);
-    }
+    if (aux->alias)
+        BIO_printf(out, "%*sAlias: %s\n", indent, "", aux->alias->data);
     if (aux->keyid) {
         BIO_printf(out, "%*sKey Id: ", indent, "");
         for (j = 0; j < aux->keyid->length; j++)
diff --git a/src/crypto/x509/x509_obj.c b/src/crypto/x509/x509_obj.c
index df54f77..80d16c1 100644
--- a/src/crypto/x509/x509_obj.c
+++ b/src/crypto/x509/x509_obj.c
@@ -64,7 +64,6 @@
 #include <openssl/x509.h>
 
 #include "../internal.h"
-#include "internal.h"
 
 
 /*
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index 848bd07..fde8bd5 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -32,7 +32,6 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
-#include "internal.h"
 #include "../internal.h"
 #include "../test/test_util.h"
 #include "../x509v3/internal.h"
@@ -1106,8 +1105,6 @@
   return stack;
 }
 
-static const time_t kReferenceTime = 1474934400 /* Sep 27th, 2016 */;
-
 static int Verify(X509 *leaf, const std::vector<X509 *> &roots,
                   const std::vector<X509 *> &intermediates,
                   const std::vector<X509_CRL *> &crls, unsigned long flags,
@@ -1150,7 +1147,7 @@
   if (param == nullptr) {
     return X509_V_ERR_UNSPECIFIED;
   }
-  X509_VERIFY_PARAM_set_time(param, kReferenceTime);
+  X509_VERIFY_PARAM_set_time(param, 1474934400 /* Sep 27th, 2016 */);
   X509_VERIFY_PARAM_set_depth(param, 16);
   if (configure_callback) {
     configure_callback(param);
@@ -1493,211 +1490,6 @@
                               {many_constraints.get()}, {}));
 }
 
-static bssl::UniquePtr<GENERAL_NAME> MakeGeneralName(int type,
-                                                     const std::string &value) {
-  if (type != GEN_EMAIL && type != GEN_DNS && type != GEN_URI) {
-    // This function only supports the IA5String types.
-    return nullptr;
-  }
-  bssl::UniquePtr<ASN1_IA5STRING> str(ASN1_IA5STRING_new());
-  bssl::UniquePtr<GENERAL_NAME> name(GENERAL_NAME_new());
-  if (!str || !name ||
-      !ASN1_STRING_set(str.get(), value.data(), value.size())) {
-    return nullptr;
-  }
-
-  name->type = type;
-  name->d.ia5 = str.release();
-  return name;
-}
-
-static bssl::UniquePtr<X509> MakeTestCert(const char *issuer,
-                                          const char *subject, EVP_PKEY *key) {
-  bssl::UniquePtr<X509> cert(X509_new());
-  if (!cert ||  //
-      !X509_set_version(cert.get(), X509_VERSION_3) ||
-      !X509_NAME_add_entry_by_txt(
-          X509_get_issuer_name(cert.get()), "CN", MBSTRING_UTF8,
-          reinterpret_cast<const uint8_t *>(issuer), -1, -1, 0) ||
-      !X509_NAME_add_entry_by_txt(
-          X509_get_subject_name(cert.get()), "CN", MBSTRING_UTF8,
-          reinterpret_cast<const uint8_t *>(subject), -1, -1, 0) ||
-      !X509_set_pubkey(cert.get(), key) ||
-      !ASN1_TIME_adj(X509_getm_notBefore(cert.get()), kReferenceTime, -1, 0) ||
-      !ASN1_TIME_adj(X509_getm_notAfter(cert.get()), kReferenceTime, 1, 0)) {
-    return nullptr;
-  }
-  return cert;
-}
-
-TEST(X509Test, NameConstraints) {
-  bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key);
-  ASSERT_TRUE(key);
-
-  const struct {
-    int type;
-    std::string name;
-    std::string constraint;
-    int result;
-  } kTests[] = {
-      // Empty string matches everything.
-      {GEN_DNS, "foo.example.com", "", X509_V_OK},
-      // Name constraints match the entire subtree.
-      {GEN_DNS, "foo.example.com", "example.com", X509_V_OK},
-      {GEN_DNS, "foo.example.com", "EXAMPLE.COM", X509_V_OK},
-      {GEN_DNS, "foo.example.com", "xample.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_DNS, "foo.example.com", "unrelated.much.longer.name.example",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      // A leading dot means at least one component must be added.
-      {GEN_DNS, "foo.example.com", ".example.com", X509_V_OK},
-      {GEN_DNS, "foo.example.com", "foo.example.com", X509_V_OK},
-      {GEN_DNS, "foo.example.com", ".foo.example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_DNS, "foo.example.com", ".xample.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_DNS, "foo.example.com", ".unrelated.much.longer.name.example",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      // NUL bytes, if not rejected, should not confuse the matching logic.
-      {GEN_DNS, std::string({'a', '\0', 'a'}), std::string({'a', '\0', 'b'}),
-       X509_V_ERR_PERMITTED_VIOLATION},
-
-      // Names must be emails.
-      {GEN_EMAIL, "not-an-email.example", "not-an-email.example",
-       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
-      // A leading dot matches all local names and all subdomains
-      {GEN_EMAIL, "foo@bar.example.com", ".example.com", X509_V_OK},
-      {GEN_EMAIL, "foo@bar.example.com", ".EXAMPLE.COM", X509_V_OK},
-      {GEN_EMAIL, "foo@bar.example.com", ".bar.example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      // Without a leading dot, the host must match exactly.
-      {GEN_EMAIL, "foo@example.com", "example.com", X509_V_OK},
-      {GEN_EMAIL, "foo@example.com", "EXAMPLE.COM", X509_V_OK},
-      {GEN_EMAIL, "foo@bar.example.com", "example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      // If the constraint specifies a mailbox, it specifies the whole thing.
-      // The halves are compared insensitively.
-      {GEN_EMAIL, "foo@example.com", "foo@example.com", X509_V_OK},
-      {GEN_EMAIL, "foo@example.com", "foo@EXAMPLE.COM", X509_V_OK},
-      {GEN_EMAIL, "foo@example.com", "FOO@example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_EMAIL, "foo@example.com", "bar@example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      // OpenSSL ignores a stray leading @.
-      {GEN_EMAIL, "foo@example.com", "@example.com", X509_V_OK},
-      {GEN_EMAIL, "foo@example.com", "@EXAMPLE.COM", X509_V_OK},
-      {GEN_EMAIL, "foo@bar.example.com", "@example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-
-      // Basic syntax check.
-      {GEN_URI, "not-a-url", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
-      {GEN_URI, "foo:not-a-url", "not-a-url",
-       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
-      {GEN_URI, "foo:/not-a-url", "not-a-url",
-       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
-      {GEN_URI, "foo:///not-a-url", "not-a-url",
-       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
-      {GEN_URI, "foo://:not-a-url", "not-a-url",
-       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
-      {GEN_URI, "foo://", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
-      // Hosts are an exact match.
-      {GEN_URI, "foo://example.com", "example.com", X509_V_OK},
-      {GEN_URI, "foo://example.com:443", "example.com", X509_V_OK},
-      {GEN_URI, "foo://example.com/whatever", "example.com", X509_V_OK},
-      {GEN_URI, "foo://bar.example.com", "example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://bar.example.com:443", "example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://bar.example.com/whatever", "example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://bar.example.com", "xample.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://bar.example.com:443", "xample.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://bar.example.com/whatever", "xample.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com", "some-other-name.example",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com:443", "some-other-name.example",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com/whatever", "some-other-name.example",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      // A leading dot allows components to be added.
-      {GEN_URI, "foo://example.com", ".example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com:443", ".example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com/whatever", ".example.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://bar.example.com", ".example.com", X509_V_OK},
-      {GEN_URI, "foo://bar.example.com:443", ".example.com", X509_V_OK},
-      {GEN_URI, "foo://bar.example.com/whatever", ".example.com", X509_V_OK},
-      {GEN_URI, "foo://example.com", ".some-other-name.example",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com:443", ".some-other-name.example",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com/whatever", ".some-other-name.example",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com", ".xample.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com:443", ".xample.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-      {GEN_URI, "foo://example.com/whatever", ".xample.com",
-       X509_V_ERR_PERMITTED_VIOLATION},
-  };
-  for (const auto &t : kTests) {
-    SCOPED_TRACE(t.type);
-    SCOPED_TRACE(t.name);
-    SCOPED_TRACE(t.constraint);
-
-    bssl::UniquePtr<GENERAL_NAME> name = MakeGeneralName(t.type, t.name);
-    ASSERT_TRUE(name);
-    bssl::UniquePtr<GENERAL_NAMES> names(GENERAL_NAMES_new());
-    ASSERT_TRUE(names);
-    ASSERT_TRUE(bssl::PushToStack(names.get(), std::move(name)));
-
-    bssl::UniquePtr<NAME_CONSTRAINTS> nc(NAME_CONSTRAINTS_new());
-    ASSERT_TRUE(nc);
-    nc->permittedSubtrees = sk_GENERAL_SUBTREE_new_null();
-    ASSERT_TRUE(nc->permittedSubtrees);
-    bssl::UniquePtr<GENERAL_SUBTREE> subtree(GENERAL_SUBTREE_new());
-    ASSERT_TRUE(subtree);
-    GENERAL_NAME_free(subtree->base);
-    subtree->base = MakeGeneralName(t.type, t.constraint).release();
-    ASSERT_TRUE(subtree->base);
-    ASSERT_TRUE(bssl::PushToStack(nc->permittedSubtrees, std::move(subtree)));
-
-    bssl::UniquePtr<X509> root = MakeTestCert("Root", "Root", key.get());
-    ASSERT_TRUE(root);
-    ASSERT_TRUE(X509_add1_ext_i2d(root.get(), NID_name_constraints, nc.get(),
-                                  /*crit=*/1, /*flags=*/0));
-    ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256()));
-
-    bssl::UniquePtr<X509> leaf = MakeTestCert("Root", "Leaf", key.get());
-    ASSERT_TRUE(leaf);
-    ASSERT_TRUE(X509_add1_ext_i2d(leaf.get(), NID_subject_alt_name, names.get(),
-                                  /*crit=*/0, /*flags=*/0));
-    ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256()));
-
-    int ret = Verify(leaf.get(), {root.get()}, {}, {}, 0);
-    EXPECT_EQ(t.result, ret) << X509_verify_cert_error_string(ret);
-  }
-}
-
-TEST(X509Test, PrintGeneralName) {
-  // TODO(https://crbug.com/boringssl/430): Add more tests. Also fix the
-  // external projects that use this to extract the SAN list and unexport.
-  bssl::UniquePtr<GENERAL_NAME> gen = MakeGeneralName(GEN_DNS, "example.com");
-  ASSERT_TRUE(gen);
-  bssl::UniquePtr<STACK_OF(CONF_VALUE)> values(
-      i2v_GENERAL_NAME(nullptr, gen.get(), nullptr));
-  ASSERT_TRUE(values);
-  ASSERT_EQ(1u, sk_CONF_VALUE_num(values.get()));
-  const CONF_VALUE *value = sk_CONF_VALUE_value(values.get(), 0);
-  EXPECT_STREQ(value->name, "DNS");
-  EXPECT_STREQ(value->value, "example.com");
-}
-
 TEST(X509Test, TestPSS) {
   bssl::UniquePtr<X509> cert(CertFromPEM(kExamplePSSCert));
   ASSERT_TRUE(cert);
@@ -2774,6 +2566,11 @@
 -----END CERTIFICATE-----
 )";
 
+/*
+
+Test cases disabled. TODO re-enable in April 2021.
+https://crbug.com/boringssl/375
+
 // kV1WithExtensionsPEM is an X.509v1 certificate with extensions.
 static const char kV1WithExtensionsPEM[] = R"(
 -----BEGIN CERTIFICATE-----
@@ -2805,6 +2602,7 @@
 BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=
 -----END CERTIFICATE-----
 )";
+*/
 
 // kV1WithIssuerUniqueIDPEM is an X.509v1 certificate with an issuerUniqueID.
 static const char kV1WithIssuerUniqueIDPEM[] = R"(
@@ -2846,8 +2644,10 @@
   EXPECT_FALSE(CertFromPEM(kNegativeVersionPEM));
   EXPECT_FALSE(CertFromPEM(kFutureVersionPEM));
   EXPECT_FALSE(CertFromPEM(kOverflowVersionPEM));
-  EXPECT_FALSE(CertFromPEM(kV1WithExtensionsPEM));
-  EXPECT_FALSE(CertFromPEM(kV2WithExtensionsPEM));
+  // Test cases disabled. TODO re-enable in April 2021.
+  // https://crbug.com/boringssl/375
+  //EXPECT_FALSE(CertFromPEM(kV1WithExtensionsPEM));
+  //EXPECT_FALSE(CertFromPEM(kV2WithExtensionsPEM));
   EXPECT_FALSE(CertFromPEM(kV1WithIssuerUniqueIDPEM));
   EXPECT_FALSE(CertFromPEM(kV1WithSubjectUniqueIDPEM));
 }
@@ -3267,7 +3067,7 @@
 
 // Test the various |X509_ATTRIBUTE| creation functions.
 TEST(X509Test, Attribute) {
-  // The friendlyName attribute has a BMPString value. See RFC 2985,
+  // The friendlyName attribute has a BMPString value. See RFC2985,
   // section 5.5.1.
   static const uint8_t kTest1[] = {0x26, 0x03};  // U+2603 SNOWMAN
   static const uint8_t kTest1UTF8[] = {0xe2, 0x98, 0x83};
diff --git a/src/crypto/x509/x509_vfy.c b/src/crypto/x509/x509_vfy.c
index 818459c..5ed4edd 100644
--- a/src/crypto/x509/x509_vfy.c
+++ b/src/crypto/x509/x509_vfy.c
@@ -1403,12 +1403,12 @@
 }
 
 /*
- * RFC 3280 says nothing about the relationship between CRL path and
+ * RFC3280 says nothing about the relationship between CRL path and
  * certificate path, which could lead to situations where a certificate could
- * be revoked or validated by a CA not authorised to do so. RFC 5280 is more
+ * be revoked or validated by a CA not authorised to do so. RFC5280 is more
  * strict and states that the two paths must end in the same trust anchor,
  * though some discussions remain... until this is resolved we use the
- * RFC 5280 version
+ * RFC5280 version
  */
 
 static int check_crl_chain(X509_STORE_CTX *ctx,
@@ -1919,8 +1919,8 @@
     int i, day, sec, ret = 0;
 
     /*
-     * Note that ASN.1 allows much more slack in the time format than RFC 5280.
-     * In RFC 5280, the representation is fixed:
+     * Note that ASN.1 allows much more slack in the time format than RFC5280.
+     * In RFC5280, the representation is fixed:
      * UTCTime: YYMMDDHHMMSSZ
      * GeneralizedTime: YYYYMMDDHHMMSSZ
      *
@@ -1976,9 +1976,9 @@
     return ret;
 }
 
-ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long offset_sec)
+ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj)
 {
-    return X509_time_adj(s, offset_sec, NULL);
+    return X509_time_adj(s, adj, NULL);
 }
 
 ASN1_TIME *X509_time_adj(ASN1_TIME *s, long offset_sec, time_t *in_tm)
@@ -1991,12 +1991,17 @@
 {
     time_t t = 0;
 
-    if (in_tm) {
+    if (in_tm)
         t = *in_tm;
-    } else {
+    else
         time(&t);
-    }
 
+    if (s && !(s->flags & ASN1_STRING_FLAG_MSTRING)) {
+        if (s->type == V_ASN1_UTCTIME)
+            return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec);
+        if (s->type == V_ASN1_GENERALIZEDTIME)
+            return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec);
+    }
     return ASN1_TIME_adj(s, t, offset_day, offset_sec);
 }
 
diff --git a/src/crypto/x509/x509name.c b/src/crypto/x509/x509name.c
index 6bc0952..0bf3459 100644
--- a/src/crypto/x509/x509name.c
+++ b/src/crypto/x509/x509name.c
@@ -64,7 +64,6 @@
 #include <openssl/x509.h>
 
 #include "../internal.h"
-#include "internal.h"
 
 
 int X509_NAME_get_text_by_NID(const X509_NAME *name, int nid, char *buf,
@@ -368,7 +367,10 @@
     if (!i)
         return (0);
     if (type != V_ASN1_UNDEF) {
-        ne->value->type = type;
+        if (type == V_ASN1_APP_CHOOSE)
+            ne->value->type = ASN1_PRINTABLE_type(bytes, len);
+        else
+            ne->value->type = type;
     }
     return (1);
 }
diff --git a/src/crypto/x509/x_all.c b/src/crypto/x509/x_all.c
index 7ceff50..65347f9 100644
--- a/src/crypto/x509/x_all.c
+++ b/src/crypto/x509/x_all.c
@@ -140,6 +140,7 @@
                              spki->signature, spki->spkac, pkey));
 }
 
+#ifndef OPENSSL_NO_FP_API
 X509 *d2i_X509_fp(FILE *fp, X509 **x509)
 {
     return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509), fp, x509);
@@ -149,6 +150,7 @@
 {
     return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509), fp, x509);
 }
+#endif
 
 X509 *d2i_X509_bio(BIO *bp, X509 **x509)
 {
@@ -160,6 +162,7 @@
     return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509), bp, x509);
 }
 
+#ifndef OPENSSL_NO_FP_API
 X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl)
 {
     return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl);
@@ -169,6 +172,7 @@
 {
     return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl);
 }
+#endif
 
 X509_CRL *d2i_X509_CRL_bio(BIO *bp, X509_CRL **crl)
 {
@@ -180,6 +184,7 @@
     return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_CRL), bp, crl);
 }
 
+#ifndef OPENSSL_NO_FP_API
 X509_REQ *d2i_X509_REQ_fp(FILE *fp, X509_REQ **req)
 {
     return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_REQ), fp, req);
@@ -189,6 +194,7 @@
 {
     return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_REQ), fp, req);
 }
+#endif
 
 X509_REQ *d2i_X509_REQ_bio(BIO *bp, X509_REQ **req)
 {
@@ -200,6 +206,7 @@
     return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_REQ), bp, req);
 }
 
+#ifndef OPENSSL_NO_FP_API
 
 #define IMPLEMENT_D2I_FP(type, name, bio_func) \
   type *name(FILE *fp, type **obj) {           \
@@ -231,6 +238,7 @@
 
 IMPLEMENT_D2I_FP(RSA, d2i_RSA_PUBKEY_fp, d2i_RSA_PUBKEY_bio)
 IMPLEMENT_I2D_FP(RSA, i2d_RSA_PUBKEY_fp, i2d_RSA_PUBKEY_bio)
+#endif
 
 #define IMPLEMENT_D2I_BIO(type, name, d2i_func)         \
   type *name(BIO *bio, type **obj) {                    \
@@ -267,11 +275,13 @@
 IMPLEMENT_I2D_BIO(RSA, i2d_RSA_PUBKEY_bio, i2d_RSA_PUBKEY)
 
 #ifndef OPENSSL_NO_DSA
+# ifndef OPENSSL_NO_FP_API
 IMPLEMENT_D2I_FP(DSA, d2i_DSAPrivateKey_fp, d2i_DSAPrivateKey_bio)
 IMPLEMENT_I2D_FP(DSA, i2d_DSAPrivateKey_fp, i2d_DSAPrivateKey_bio)
 
 IMPLEMENT_D2I_FP(DSA, d2i_DSA_PUBKEY_fp, d2i_DSA_PUBKEY_bio)
 IMPLEMENT_I2D_FP(DSA, i2d_DSA_PUBKEY_fp, i2d_DSA_PUBKEY_bio)
+# endif
 
 IMPLEMENT_D2I_BIO(DSA, d2i_DSAPrivateKey_bio, d2i_DSAPrivateKey)
 IMPLEMENT_I2D_BIO(DSA, i2d_DSAPrivateKey_bio, i2d_DSAPrivateKey)
@@ -280,11 +290,13 @@
 IMPLEMENT_I2D_BIO(DSA, i2d_DSA_PUBKEY_bio, i2d_DSA_PUBKEY)
 #endif
 
+#ifndef OPENSSL_NO_FP_API
 IMPLEMENT_D2I_FP(EC_KEY, d2i_ECPrivateKey_fp, d2i_ECPrivateKey_bio)
 IMPLEMENT_I2D_FP(EC_KEY, i2d_ECPrivateKey_fp, i2d_ECPrivateKey_bio)
 
 IMPLEMENT_D2I_FP(EC_KEY, d2i_EC_PUBKEY_fp, d2i_EC_PUBKEY_bio)
 IMPLEMENT_I2D_FP(EC_KEY, i2d_EC_PUBKEY_fp, i2d_EC_PUBKEY_bio)
+#endif
 
 IMPLEMENT_D2I_BIO(EC_KEY, d2i_ECPrivateKey_bio, d2i_ECPrivateKey)
 IMPLEMENT_I2D_BIO(EC_KEY, i2d_ECPrivateKey_bio, i2d_ECPrivateKey)
@@ -330,12 +342,15 @@
             (ASN1_ITEM_rptr(X509_NAME), type, (char *)data, md, len));
 }
 
+#ifndef OPENSSL_NO_FP_API
 IMPLEMENT_D2I_FP(X509_SIG, d2i_PKCS8_fp, d2i_PKCS8_bio)
 IMPLEMENT_I2D_FP(X509_SIG, i2d_PKCS8_fp, i2d_PKCS8_bio)
+#endif
 
 IMPLEMENT_D2I_BIO(X509_SIG, d2i_PKCS8_bio, d2i_X509_SIG)
 IMPLEMENT_I2D_BIO(X509_SIG, i2d_PKCS8_bio, i2d_X509_SIG)
 
+#ifndef OPENSSL_NO_FP_API
 IMPLEMENT_D2I_FP(PKCS8_PRIV_KEY_INFO, d2i_PKCS8_PRIV_KEY_INFO_fp,
                  d2i_PKCS8_PRIV_KEY_INFO_bio)
 IMPLEMENT_I2D_FP(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO_fp,
@@ -375,6 +390,7 @@
     PKCS8_PRIV_KEY_INFO_free(p8inf);
     return ret;
 }
+#endif
 
 IMPLEMENT_D2I_BIO(EVP_PKEY, d2i_PrivateKey_bio, d2i_AutoPrivateKey)
 IMPLEMENT_I2D_BIO(EVP_PKEY, i2d_PrivateKey_bio, i2d_PrivateKey)
diff --git a/src/crypto/x509/x_name.c b/src/crypto/x509/x_name.c
index 4fea082..e56f2c3 100644
--- a/src/crypto/x509/x_name.c
+++ b/src/crypto/x509/x_name.c
@@ -68,7 +68,6 @@
 
 #include "../asn1/internal.h"
 #include "../internal.h"
-#include "internal.h"
 
 
 typedef STACK_OF(X509_NAME_ENTRY) STACK_OF_X509_NAME_ENTRY;
@@ -261,13 +260,17 @@
 static int x509_name_ex_i2d(ASN1_VALUE **val, unsigned char **out,
                             const ASN1_ITEM *it, int tag, int aclass)
 {
+    int ret;
     X509_NAME *a = (X509_NAME *)*val;
-    if (a->modified &&
-        (!x509_name_encode(a) ||
-         !x509_name_canon(a))) {
-        return -1;
+    if (a->modified) {
+        ret = x509_name_encode(a);
+        if (ret < 0)
+            return ret;
+        ret = x509_name_canon(a);
+        if (ret < 0)
+            return ret;
     }
-    int ret = a->bytes->length;
+    ret = a->bytes->length;
     if (out != NULL) {
         OPENSSL_memcpy(*out, a->bytes->data, ret);
         *out += ret;
@@ -303,29 +306,22 @@
             goto memerr;
     }
     ASN1_VALUE *intname_val = (ASN1_VALUE *)intname;
-    len =
-        ASN1_item_ex_i2d(&intname_val, NULL, ASN1_ITEM_rptr(X509_NAME_INTERNAL),
-                         /*tag=*/-1, /*aclass=*/0);
-    if (len <= 0) {
-      goto err;
-    }
+    len = ASN1_item_ex_i2d(&intname_val, NULL,
+                           ASN1_ITEM_rptr(X509_NAME_INTERNAL), -1, -1);
     if (!BUF_MEM_grow(a->bytes, len))
         goto memerr;
     p = (unsigned char *)a->bytes->data;
-    if (ASN1_item_ex_i2d(&intname_val, &p, ASN1_ITEM_rptr(X509_NAME_INTERNAL),
-                         /*tag=*/-1, /*aclass=*/0) <= 0) {
-        goto err;
-    }
+    ASN1_item_ex_i2d(&intname_val,
+                     &p, ASN1_ITEM_rptr(X509_NAME_INTERNAL), -1, -1);
     sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname,
                                          local_sk_X509_NAME_ENTRY_free);
     a->modified = 0;
-    return 1;
+    return len;
  memerr:
-    OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
-err:
     sk_STACK_OF_X509_NAME_ENTRY_pop_free(intname,
                                          local_sk_X509_NAME_ENTRY_free);
-    return 0;
+    OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
+    return -1;
 }
 
 /*
@@ -507,8 +503,8 @@
     len = 0;
     for (i = 0; i < sk_ASN1_VALUE_num(intname); i++) {
         v = sk_ASN1_VALUE_value(intname, i);
-        ltmp = ASN1_item_ex_i2d(&v, in, ASN1_ITEM_rptr(X509_NAME_ENTRIES),
-                                /*tag=*/-1, /*aclass=*/0);
+        ltmp = ASN1_item_ex_i2d(&v, in,
+                                ASN1_ITEM_rptr(X509_NAME_ENTRIES), -1, -1);
         if (ltmp < 0)
             return ltmp;
         len += ltmp;
diff --git a/src/crypto/x509/x_x509.c b/src/crypto/x509/x_x509.c
index 9d350bd..f6b63b6 100644
--- a/src/crypto/x509/x_x509.c
+++ b/src/crypto/x509/x_x509.c
@@ -69,7 +69,6 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
-#include "internal.h"
 
 static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
 
@@ -129,18 +128,20 @@
             }
         }
 
-        /* Per RFC 5280, section 4.1.2.8, these fields require v2 or v3. */
+        /* Per RFC5280, section 4.1.2.8, these fields require v2 or v3. */
         if (version == 0 && (ret->cert_info->issuerUID != NULL ||
                              ret->cert_info->subjectUID != NULL)) {
             OPENSSL_PUT_ERROR(X509, X509_R_INVALID_FIELD_FOR_VERSION);
             return 0;
         }
 
-        /* Per RFC 5280, section 4.1.2.9, extensions require v3. */
+        /* Per RFC5280, section 4.1.2.9, extensions require v3. */
+        /* Check disabled. TODO re-enable in April 2021.
+           https://crbug.com/boringssl/375
         if (version != 2 && ret->cert_info->extensions != NULL) {
             OPENSSL_PUT_ERROR(X509, X509_R_INVALID_FIELD_FOR_VERSION);
             return 0;
-        }
+        }*/
 
         break;
     }
@@ -289,15 +290,13 @@
         return length;
     }
 
-    if (a->aux != NULL) {
-        tmplen = i2d_X509_CERT_AUX(a->aux, pp);
-        if (tmplen < 0) {
-            if (start != NULL)
-                *pp = start;
-            return tmplen;
-        }
-        length += tmplen;
+    tmplen = i2d_X509_CERT_AUX(a->aux, pp);
+    if (tmplen < 0) {
+        if (start != NULL)
+            *pp = start;
+        return tmplen;
     }
+    length += tmplen;
 
     return length;
 }
diff --git a/src/crypto/x509v3/internal.h b/src/crypto/x509v3/internal.h
index df7d813..e510b40 100644
--- a/src/crypto/x509v3/internal.h
+++ b/src/crypto/x509v3/internal.h
@@ -17,8 +17,6 @@
 
 #include <openssl/base.h>
 
-#include <openssl/conf.h>
-
 #if defined(__cplusplus)
 extern "C" {
 #endif
@@ -62,20 +60,6 @@
 // to all 16 bytes of |ipout| and returns 16. Otherwise, it returns zero.
 int x509v3_a2i_ipadd(unsigned char ipout[16], const char *ipasc);
 
-// A |BIT_STRING_BITNAME| is used to contain a list of bit names.
-typedef struct {
-  int bitnum;
-  const char *lname;
-  const char *sname;
-} BIT_STRING_BITNAME;
-
-// x509V3_add_value_asn1_string appends a |CONF_VALUE| with the specified name
-// and value to |*extlist|. if |*extlist| is NULL, it sets |*extlist| to a
-// newly-allocated |STACK_OF(CONF_VALUE)| first. It returns one on success and
-// zero on error.
-int x509V3_add_value_asn1_string(const char *name, const ASN1_STRING *value,
-                                 STACK_OF(CONF_VALUE) **extlist);
-
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/src/crypto/x509v3/pcy_cache.c b/src/crypto/x509v3/pcy_cache.c
index 1caea76..755c079 100644
--- a/src/crypto/x509v3/pcy_cache.c
+++ b/src/crypto/x509v3/pcy_cache.c
@@ -62,7 +62,6 @@
 
 #include "pcy_int.h"
 #include "../internal.h"
-#include "../x509/internal.h"
 
 static int policy_data_cmp(const X509_POLICY_DATA **a,
                            const X509_POLICY_DATA **b);
diff --git a/src/crypto/x509v3/pcy_data.c b/src/crypto/x509v3/pcy_data.c
index c4a56ca..58584c2 100644
--- a/src/crypto/x509v3/pcy_data.c
+++ b/src/crypto/x509v3/pcy_data.c
@@ -79,7 +79,7 @@
 /*
  * Create a data based on an existing policy. If 'id' is NULL use the oid in
  * the policy, otherwise use 'id'. This behaviour covers the two types of
- * data in RFC 3280: data with from a CertificatePolcies extension and
+ * data in RFC3280: data with from a CertificatePolcies extension and
  * additional data with just the qualifiers of anyPolicy and ID from another
  * source.
  */
diff --git a/src/crypto/x509v3/pcy_int.h b/src/crypto/x509v3/pcy_int.h
index aee71d6..fc6e20a 100644
--- a/src/crypto/x509v3/pcy_int.h
+++ b/src/crypto/x509v3/pcy_int.h
@@ -65,7 +65,7 @@
 
 /*
  * This structure and the field names correspond to the Policy 'node' of
- * RFC 3280. NB this structure contains no pointers to parent or child data:
+ * RFC3280. NB this structure contains no pointers to parent or child data:
  * X509_POLICY_NODE contains that. This means that the main policy data can
  * be kept static and cached with the certificate.
  */
diff --git a/src/crypto/x509v3/pcy_map.c b/src/crypto/x509v3/pcy_map.c
index a4a3601..7263c69 100644
--- a/src/crypto/x509v3/pcy_map.c
+++ b/src/crypto/x509v3/pcy_map.c
@@ -62,7 +62,6 @@
 #include <openssl/x509v3.h>
 
 #include "pcy_int.h"
-#include "../x509/internal.h"
 
 /*
  * Set policy mapping entries in cache. Note: this modifies the passed
diff --git a/src/crypto/x509v3/pcy_tree.c b/src/crypto/x509v3/pcy_tree.c
index 596266b..136b45f 100644
--- a/src/crypto/x509v3/pcy_tree.c
+++ b/src/crypto/x509v3/pcy_tree.c
@@ -67,7 +67,6 @@
 
 #include "pcy_int.h"
 #include "../internal.h"
-#include "../x509/internal.h"
 
 /*
  * Enable this to print out the complete policy tree at various point during
@@ -333,7 +332,7 @@
 }
 
 /*
- * This corresponds to RFC 3280 6.1.3(d)(1): link any data from
+ * This corresponds to RFC3280 6.1.3(d)(1): link any data from
  * CertificatePolicies onto matching parent or anyPolicy if no match.
  */
 
@@ -366,7 +365,7 @@
 }
 
 /*
- * This corresponds to RFC 3280 6.1.3(d)(2): Create new data for any unmatched
+ * This corresponds to RFC3280 6.1.3(d)(2): Create new data for any unmatched
  * policies in the parent and link to anyPolicy.
  */
 
@@ -501,7 +500,7 @@
     if (curr->flags & X509_V_FLAG_INHIBIT_MAP) {
         for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--) {
             node = sk_X509_POLICY_NODE_value(nodes, i);
-            /* Delete any mapped data: see RFC 3280 XXXX */
+            /* Delete any mapped data: see RFC3280 XXXX */
             if (node->data->flags & POLICY_DATA_FLAG_MAP_MASK) {
                 node->parent->nchild--;
                 OPENSSL_free(node);
diff --git a/src/crypto/x509v3/v3_akey.c b/src/crypto/x509v3/v3_akey.c
index 0aba20e..1037673 100644
--- a/src/crypto/x509v3/v3_akey.c
+++ b/src/crypto/x509v3/v3_akey.c
@@ -93,39 +93,20 @@
                                                  STACK_OF(CONF_VALUE)
                                                  *extlist)
 {
-    char *tmp = NULL;
-    int extlist_was_null = extlist == NULL;
+    char *tmp;
     if (akeyid->keyid) {
         tmp = x509v3_bytes_to_hex(akeyid->keyid->data, akeyid->keyid->length);
-        int ok = tmp != NULL && X509V3_add_value("keyid", tmp, &extlist);
+        X509V3_add_value("keyid", tmp, &extlist);
         OPENSSL_free(tmp);
-        if (!ok) {
-            goto err;
-        }
     }
-    if (akeyid->issuer) {
-        STACK_OF(CONF_VALUE) *tmpextlist =
-            i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist);
-        if (tmpextlist == NULL) {
-            goto err;
-        }
-        extlist = tmpextlist;
-    }
+    if (akeyid->issuer)
+        extlist = i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist);
     if (akeyid->serial) {
         tmp = x509v3_bytes_to_hex(akeyid->serial->data, akeyid->serial->length);
-        int ok = tmp != NULL && X509V3_add_value("serial", tmp, &extlist);
+        X509V3_add_value("serial", tmp, &extlist);
         OPENSSL_free(tmp);
-        if (!ok) {
-            goto err;
-        }
     }
     return extlist;
-
-err:
-    if (extlist_was_null) {
-        sk_CONF_VALUE_pop_free(extlist, X509V3_conf_free);
-    }
-    return NULL;
 }
 
 /*
diff --git a/src/crypto/x509v3/v3_alt.c b/src/crypto/x509v3/v3_alt.c
index 0c55816..4d54075 100644
--- a/src/crypto/x509v3/v3_alt.c
+++ b/src/crypto/x509v3/v3_alt.c
@@ -104,17 +104,11 @@
                                         GENERAL_NAMES *gens,
                                         STACK_OF(CONF_VALUE) *ret)
 {
-    int ret_was_null = ret == NULL;
-    for (size_t i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
-        GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, i);
-        STACK_OF(CONF_VALUE) *tmp = i2v_GENERAL_NAME(method, gen, ret);
-        if (tmp == NULL) {
-            if (ret_was_null) {
-                sk_CONF_VALUE_pop_free(ret, X509V3_conf_free);
-            }
-            return NULL;
-        }
-        ret = tmp;
+    size_t i;
+    GENERAL_NAME *gen;
+    for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
+        gen = sk_GENERAL_NAME_value(gens, i);
+        ret = i2v_GENERAL_NAME(method, gen, ret);
     }
     if (!ret)
         return sk_CONF_VALUE_new_null();
@@ -125,9 +119,6 @@
                                        GENERAL_NAME *gen,
                                        STACK_OF(CONF_VALUE) *ret)
 {
-    /* Note the error-handling for this function relies on there being at most
-     * one |X509V3_add_value| call. If there were two and the second failed, we
-     * would need to sometimes free the first call's result. */
     unsigned char *p;
     char oline[256], htmp[5];
     int i;
@@ -148,17 +139,17 @@
         break;
 
     case GEN_EMAIL:
-        if (!x509V3_add_value_asn1_string("email", gen->d.ia5, &ret))
+        if (!X509V3_add_value_uchar("email", gen->d.ia5->data, &ret))
             return NULL;
         break;
 
     case GEN_DNS:
-        if (!x509V3_add_value_asn1_string("DNS", gen->d.ia5, &ret))
+        if (!X509V3_add_value_uchar("DNS", gen->d.ia5->data, &ret))
             return NULL;
         break;
 
     case GEN_URI:
-        if (!x509V3_add_value_asn1_string("URI", gen->d.ia5, &ret))
+        if (!X509V3_add_value_uchar("URI", gen->d.ia5->data, &ret))
             return NULL;
         break;
 
diff --git a/src/crypto/x509v3/v3_bitst.c b/src/crypto/x509v3/v3_bitst.c
index 871b776..402f830 100644
--- a/src/crypto/x509v3/v3_bitst.c
+++ b/src/crypto/x509v3/v3_bitst.c
@@ -63,9 +63,6 @@
 #include <openssl/obj.h>
 #include <openssl/x509v3.h>
 
-#include "internal.h"
-
-
 static const BIT_STRING_BITNAME ns_cert_type_table[] = {
     {0, "SSL Client", "client"},
     {1, "SSL Server", "server"},
diff --git a/src/crypto/x509v3/v3_cpols.c b/src/crypto/x509v3/v3_cpols.c
index 9f66f47..a2ed01c 100644
--- a/src/crypto/x509v3/v3_cpols.c
+++ b/src/crypto/x509v3/v3_cpols.c
@@ -432,8 +432,8 @@
         qualinfo = sk_POLICYQUALINFO_value(quals, i);
         switch (OBJ_obj2nid(qualinfo->pqualid)) {
         case NID_id_qt_cps:
-            BIO_printf(out, "%*sCPS: %.*s\n", indent, "",
-                       qualinfo->d.cpsuri->length, qualinfo->d.cpsuri->data);
+            BIO_printf(out, "%*sCPS: %s\n", indent, "",
+                       qualinfo->d.cpsuri->data);
             break;
 
         case NID_id_qt_unotice:
@@ -457,8 +457,8 @@
     if (notice->noticeref) {
         NOTICEREF *ref;
         ref = notice->noticeref;
-        BIO_printf(out, "%*sOrganization: %.*s\n", indent, "",
-                   ref->organization->length, ref->organization->data);
+        BIO_printf(out, "%*sOrganization: %s\n", indent, "",
+                   ref->organization->data);
         BIO_printf(out, "%*sNumber%s: ", indent, "",
                    sk_ASN1_INTEGER_num(ref->noticenos) > 1 ? "s" : "");
         for (i = 0; i < sk_ASN1_INTEGER_num(ref->noticenos); i++) {
@@ -480,8 +480,8 @@
         BIO_puts(out, "\n");
     }
     if (notice->exptext)
-        BIO_printf(out, "%*sExplicit Text: %.*s\n", indent, "",
-                   notice->exptext->length, notice->exptext->data);
+        BIO_printf(out, "%*sExplicit Text: %s\n", indent, "",
+                   notice->exptext->data);
 }
 
 void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent)
diff --git a/src/crypto/x509v3/v3_crld.c b/src/crypto/x509v3/v3_crld.c
index 93e5b6d..27ccbc1 100644
--- a/src/crypto/x509v3/v3_crld.c
+++ b/src/crypto/x509v3/v3_crld.c
@@ -66,10 +66,6 @@
 #include <openssl/obj.h>
 #include <openssl/x509v3.h>
 
-#include "internal.h"
-#include "../x509/internal.h"
-
-
 static void *v2i_crld(const X509V3_EXT_METHOD *method,
                       X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
 static int i2r_crldp(const X509V3_EXT_METHOD *method, void *pcrldp, BIO *out,
diff --git a/src/crypto/x509v3/v3_enum.c b/src/crypto/x509v3/v3_enum.c
index 9b222bb..3a9d4d6 100644
--- a/src/crypto/x509v3/v3_enum.c
+++ b/src/crypto/x509v3/v3_enum.c
@@ -61,11 +61,6 @@
 #include <openssl/mem.h>
 #include <openssl/x509v3.h>
 
-#include "internal.h"
-
-
-typedef BIT_STRING_BITNAME ENUMERATED_NAMES;
-
 static const ENUMERATED_NAMES crl_reasons[] = {
     {CRL_REASON_UNSPECIFIED, "Unspecified", "unspecified"},
     {CRL_REASON_KEY_COMPROMISE, "Key Compromise", "keyCompromise"},
diff --git a/src/crypto/x509v3/v3_ncons.c b/src/crypto/x509v3/v3_ncons.c
index 739a59e..593a520 100644
--- a/src/crypto/x509v3/v3_ncons.c
+++ b/src/crypto/x509v3/v3_ncons.c
@@ -66,7 +66,6 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
-#include "../x509/internal.h"
 
 
 static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
@@ -390,73 +389,25 @@
     return X509_V_OK;
 }
 
-static int starts_with(const CBS *cbs, uint8_t c)
-{
-    return CBS_len(cbs) > 0 && CBS_data(cbs)[0] == c;
-}
-
-static int equal_case(const CBS *a, const CBS *b)
-{
-    if (CBS_len(a) != CBS_len(b)) {
-        return 0;
-    }
-    /* Note we cannot use |OPENSSL_strncasecmp| because that would stop
-     * iterating at NUL. */
-    const uint8_t *a_data = CBS_data(a), *b_data = CBS_data(b);
-    for (size_t i = 0; i < CBS_len(a); i++) {
-        if (OPENSSL_tolower(a_data[i]) != OPENSSL_tolower(b_data[i])) {
-            return 0;
-        }
-    }
-    return 1;
-}
-
-static int has_suffix_case(const CBS *a, const CBS *b)
-{
-    if (CBS_len(a) < CBS_len(b)) {
-        return 0;
-    }
-    CBS copy = *a;
-    CBS_skip(&copy, CBS_len(a) - CBS_len(b));
-    return equal_case(&copy, b);
-}
-
 static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
 {
-    CBS dns_cbs, base_cbs;
-    CBS_init(&dns_cbs, dns->data, dns->length);
-    CBS_init(&base_cbs, base->data, base->length);
-
+    char *baseptr = (char *)base->data;
+    char *dnsptr = (char *)dns->data;
     /* Empty matches everything */
-    if (CBS_len(&base_cbs) == 0) {
+    if (!*baseptr)
         return X509_V_OK;
-    }
-
-    /* If |base_cbs| begins with a '.', do a simple suffix comparison. This is
-     * not part of RFC5280, but is part of OpenSSL's original behavior. */
-    if (starts_with(&base_cbs, '.')) {
-        if (has_suffix_case(&dns_cbs, &base_cbs)) {
-            return X509_V_OK;
-        }
-        return X509_V_ERR_PERMITTED_VIOLATION;
-    }
-
     /*
      * Otherwise can add zero or more components on the left so compare RHS
      * and if dns is longer and expect '.' as preceding character.
      */
-    if (CBS_len(&dns_cbs) > CBS_len(&base_cbs)) {
-        uint8_t dot;
-        if (!CBS_skip(&dns_cbs, CBS_len(&dns_cbs) - CBS_len(&base_cbs) - 1) ||
-            !CBS_get_u8(&dns_cbs, &dot) ||
-            dot != '.') {
+    if (dns->length > base->length) {
+        dnsptr += dns->length - base->length;
+        if (*baseptr != '.' && dnsptr[-1] != '.')
             return X509_V_ERR_PERMITTED_VIOLATION;
-        }
     }
 
-    if (!equal_case(&dns_cbs, &base_cbs)) {
+    if (OPENSSL_strcasecmp(baseptr, dnsptr))
         return X509_V_ERR_PERMITTED_VIOLATION;
-    }
 
     return X509_V_OK;
 
@@ -464,94 +415,86 @@
 
 static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
 {
-    CBS eml_cbs, base_cbs;
-    CBS_init(&eml_cbs, eml->data, eml->length);
-    CBS_init(&base_cbs, base->data, base->length);
+    const char *baseptr = (char *)base->data;
+    const char *emlptr = (char *)eml->data;
 
-    /* TODO(davidben): In OpenSSL 1.1.1, this switched from the first '@' to the
-     * last one. Match them here, or perhaps do an actual parse. Looks like
-     * multiple '@'s may be allowed in quoted strings. */
-    CBS eml_local, base_local;
-    if (!CBS_get_until_first(&eml_cbs, &eml_local, '@')) {
+    const char *baseat = strchr(baseptr, '@');
+    const char *emlat = strchr(emlptr, '@');
+    if (!emlat)
         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
-    }
-    int base_has_at = CBS_get_until_first(&base_cbs, &base_local, '@');
-
     /* Special case: inital '.' is RHS match */
-    if (!base_has_at && starts_with(&base_cbs, '.')) {
-        if (has_suffix_case(&eml_cbs, &base_cbs)) {
-            return X509_V_OK;
+    if (!baseat && (*baseptr == '.')) {
+        if (eml->length > base->length) {
+            emlptr += eml->length - base->length;
+            if (!OPENSSL_strcasecmp(baseptr, emlptr))
+                return X509_V_OK;
         }
         return X509_V_ERR_PERMITTED_VIOLATION;
     }
 
     /* If we have anything before '@' match local part */
-    if (base_has_at) {
-        /* TODO(davidben): This interprets a constraint of "@example.com" as
-         * "example.com", which is not part of RFC5280. */
-        if (CBS_len(&base_local) > 0) {
-            /* Case sensitive match of local part */
-            if (!CBS_mem_equal(&base_local, CBS_data(&eml_local),
-                               CBS_len(&eml_local))) {
+
+    if (baseat) {
+        if (baseat != baseptr) {
+            if ((baseat - baseptr) != (emlat - emlptr))
                 return X509_V_ERR_PERMITTED_VIOLATION;
-            }
+            /* Case sensitive match of local part */
+            if (strncmp(baseptr, emlptr, emlat - emlptr))
+                return X509_V_ERR_PERMITTED_VIOLATION;
         }
         /* Position base after '@' */
-        assert(starts_with(&base_cbs, '@'));
-        CBS_skip(&base_cbs, 1);
+        baseptr = baseat + 1;
     }
-
+    emlptr = emlat + 1;
     /* Just have hostname left to match: case insensitive */
-    assert(starts_with(&eml_cbs, '@'));
-    CBS_skip(&eml_cbs, 1);
-    if (!equal_case(&base_cbs, &eml_cbs)) {
+    if (OPENSSL_strcasecmp(baseptr, emlptr))
         return X509_V_ERR_PERMITTED_VIOLATION;
-    }
 
     return X509_V_OK;
+
 }
 
 static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
 {
-    CBS uri_cbs, base_cbs;
-    CBS_init(&uri_cbs, uri->data, uri->length);
-    CBS_init(&base_cbs, base->data, base->length);
-
+    const char *baseptr = (char *)base->data;
+    const char *hostptr = (char *)uri->data;
+    const char *p = strchr(hostptr, ':');
+    int hostlen;
     /* Check for foo:// and skip past it */
-    CBS scheme;
-    uint8_t byte;
-    if (!CBS_get_until_first(&uri_cbs, &scheme, ':') ||
-        !CBS_skip(&uri_cbs, 1) ||  // Skip the colon
-        !CBS_get_u8(&uri_cbs, &byte) || byte != '/' ||
-        !CBS_get_u8(&uri_cbs, &byte) || byte != '/') {
+    if (!p || (p[1] != '/') || (p[2] != '/'))
         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
-    }
+    hostptr = p + 3;
 
-    /* Look for a port indicator as end of hostname first. Otherwise look for
-     * trailing slash, or the end of the string.
-     * TODO(davidben): This is not a correct URI parser and mishandles IPv6
-     * literals. */
-    CBS host;
-    if (!CBS_get_until_first(&uri_cbs, &host, ':') &&
-        !CBS_get_until_first(&uri_cbs, &host, '/')) {
-        host = uri_cbs;
-    }
+    /* Determine length of hostname part of URI */
 
-    if (CBS_len(&host) == 0) {
+    /* Look for a port indicator as end of hostname first */
+
+    p = strchr(hostptr, ':');
+    /* Otherwise look for trailing slash */
+    if (!p)
+        p = strchr(hostptr, '/');
+
+    if (!p)
+        hostlen = strlen(hostptr);
+    else
+        hostlen = p - hostptr;
+
+    if (hostlen == 0)
         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
-    }
 
     /* Special case: inital '.' is RHS match */
-    if (starts_with(&base_cbs, '.')) {
-        if (has_suffix_case(&host, &base_cbs)) {
-            return X509_V_OK;
+    if (*baseptr == '.') {
+        if (hostlen > base->length) {
+            p = hostptr + hostlen - base->length;
+            if (!OPENSSL_strncasecmp(p, baseptr, base->length))
+                return X509_V_OK;
         }
         return X509_V_ERR_PERMITTED_VIOLATION;
     }
 
-    if (!equal_case(&base_cbs, &host)) {
+    if ((base->length != (int)hostlen)
+        || OPENSSL_strncasecmp(hostptr, baseptr, hostlen))
         return X509_V_ERR_PERMITTED_VIOLATION;
-    }
 
     return X509_V_OK;
 
diff --git a/src/crypto/x509v3/v3_pci.c b/src/crypto/x509v3/v3_pci.c
index 57b64ef..f9031c0 100644
--- a/src/crypto/x509v3/v3_pci.c
+++ b/src/crypto/x509v3/v3_pci.c
@@ -75,8 +75,7 @@
     i2a_ASN1_OBJECT(out, pci->proxyPolicy->policyLanguage);
     BIO_puts(out, "\n");
     if (pci->proxyPolicy->policy && pci->proxyPolicy->policy->data)
-        BIO_printf(out, "%*sPolicy Text: %.*s\n", indent, "",
-                   pci->proxyPolicy->policy->length,
+        BIO_printf(out, "%*sPolicy Text: %s\n", indent, "",
                    pci->proxyPolicy->policy->data);
     return 1;
 }
diff --git a/src/crypto/x509v3/v3_prn.c b/src/crypto/x509v3/v3_prn.c
index ee4c482..79e96e6 100644
--- a/src/crypto/x509v3/v3_prn.c
+++ b/src/crypto/x509v3/v3_prn.c
@@ -218,6 +218,7 @@
     }
 }
 
+#ifndef OPENSSL_NO_FP_API
 int X509V3_EXT_print_fp(FILE *fp, X509_EXTENSION *ext, int flag, int indent)
 {
     BIO *bio_tmp;
@@ -228,3 +229,4 @@
     BIO_free(bio_tmp);
     return ret;
 }
+#endif
diff --git a/src/crypto/x509v3/v3_purp.c b/src/crypto/x509v3/v3_purp.c
index d1f56f0..2d7a4db 100644
--- a/src/crypto/x509v3/v3_purp.c
+++ b/src/crypto/x509v3/v3_purp.c
@@ -68,7 +68,6 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
-#include "../x509/internal.h"
 #include "internal.h"
 
 #define V1_ROOT (EXFLAG_V1|EXFLAG_SS)
diff --git a/src/crypto/x509v3/v3_utl.c b/src/crypto/x509v3/v3_utl.c
index 5d91aed..fd6e689 100644
--- a/src/crypto/x509v3/v3_utl.c
+++ b/src/crypto/x509v3/v3_utl.c
@@ -88,69 +88,42 @@
 
 /* Add a CONF_VALUE name value pair to stack */
 
-static int x509V3_add_len_value(const char *name, const char *value,
-                                size_t value_len, int omit_value,
-                                STACK_OF(CONF_VALUE) **extlist)
+int X509V3_add_value(const char *name, const char *value,
+                     STACK_OF(CONF_VALUE) **extlist)
 {
     CONF_VALUE *vtmp = NULL;
     char *tname = NULL, *tvalue = NULL;
-    int extlist_was_null = *extlist == NULL;
     if (name && !(tname = OPENSSL_strdup(name)))
-        goto malloc_err;
-    if (!omit_value) {
-        /* |CONF_VALUE| cannot represent strings with NULs. */
-        if (OPENSSL_memchr(value, 0, value_len)) {
-            OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_VALUE);
-            goto err;
-        }
-        tvalue = OPENSSL_strndup(value, value_len);
-        if (tvalue == NULL) {
-            goto malloc_err;
-        }
-    }
+        goto err;
+    if (value && !(tvalue = OPENSSL_strdup(value)))
+        goto err;
     if (!(vtmp = CONF_VALUE_new()))
-        goto malloc_err;
+        goto err;
     if (!*extlist && !(*extlist = sk_CONF_VALUE_new_null()))
-        goto malloc_err;
+        goto err;
     vtmp->section = NULL;
     vtmp->name = tname;
     vtmp->value = tvalue;
     if (!sk_CONF_VALUE_push(*extlist, vtmp))
-        goto malloc_err;
+        goto err;
     return 1;
- malloc_err:
-    OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
  err:
-    if (extlist_was_null) {
-        sk_CONF_VALUE_free(*extlist);
-        *extlist = NULL;
-    }
-    OPENSSL_free(vtmp);
-    OPENSSL_free(tname);
-    OPENSSL_free(tvalue);
+    OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
+    if (vtmp)
+        OPENSSL_free(vtmp);
+    if (tname)
+        OPENSSL_free(tname);
+    if (tvalue)
+        OPENSSL_free(tvalue);
     return 0;
 }
 
-int X509V3_add_value(const char *name, const char *value,
-                     STACK_OF(CONF_VALUE) **extlist)
-{
-    return x509V3_add_len_value(name, value, value != NULL ? strlen(value) : 0,
-                                /*omit_value=*/value == NULL, extlist);
-}
-
 int X509V3_add_value_uchar(const char *name, const unsigned char *value,
                            STACK_OF(CONF_VALUE) **extlist)
 {
     return X509V3_add_value(name, (const char *)value, extlist);
 }
 
-int x509V3_add_value_asn1_string(const char *name, const ASN1_STRING *value,
-                                 STACK_OF(CONF_VALUE) **extlist)
-{
-    return x509V3_add_len_value(name, (const char *)value->data, value->length,
-                                /*omit_value=*/0, extlist);
-}
-
 /* Free function for STACK_OF(CONF_VALUE) */
 
 void X509V3_conf_free(CONF_VALUE *conf)
@@ -295,7 +268,7 @@
     return aint;
 }
 
-int X509V3_add_value_int(const char *name, const ASN1_INTEGER *aint,
+int X509V3_add_value_int(const char *name, ASN1_INTEGER *aint,
                          STACK_OF(CONF_VALUE) **extlist)
 {
     char *strtmp;
@@ -658,45 +631,27 @@
 
 static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, ASN1_IA5STRING *email)
 {
+    char *emtmp;
     /* First some sanity checks */
     if (email->type != V_ASN1_IA5STRING)
         return 1;
-    if (email->data == NULL || email->length == 0)
+    if (!email->data || !email->length)
         return 1;
-    /* |OPENSSL_STRING| cannot represent strings with embedded NULs. Do not
-     * report them as outputs. */
-    if (OPENSSL_memchr(email->data, 0, email->length) != NULL)
-        return 1;
-
-    char *emtmp = NULL;
     if (!*sk)
         *sk = sk_OPENSSL_STRING_new(sk_strcmp);
     if (!*sk)
-        goto err;
-
-    emtmp = OPENSSL_strndup((char *)email->data, email->length);
-    if (emtmp == NULL) {
-        goto err;
-    }
-
+        return 0;
     /* Don't add duplicates */
     sk_OPENSSL_STRING_sort(*sk);
-    if (sk_OPENSSL_STRING_find(*sk, NULL, emtmp)) {
-        OPENSSL_free(emtmp);
+    if (sk_OPENSSL_STRING_find(*sk, NULL, (char *)email->data))
         return 1;
-    }
-    if (!sk_OPENSSL_STRING_push(*sk, emtmp)) {
-        goto err;
+    emtmp = OPENSSL_strdup((char *)email->data);
+    if (!emtmp || !sk_OPENSSL_STRING_push(*sk, emtmp)) {
+        X509_email_free(*sk);
+        *sk = NULL;
+        return 0;
     }
     return 1;
-
-err:
-    /* TODO(davidben): Fix the error-handling in this file. It currently relies
-     * on |append_ia5| leaving |*sk| at NULL on error. */
-    OPENSSL_free(emtmp);
-    X509_email_free(*sk);
-    *sk = NULL;
-    return 0;
 }
 
 void X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
@@ -1165,7 +1120,7 @@
 
 /*
  * Convert IP addresses both IPv4 and IPv6 into an OCTET STRING compatible
- * with RFC 3280.
+ * with RFC3280.
  */
 
 ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc)
diff --git a/src/decrepit/evp/evp_do_all.c b/src/decrepit/evp/evp_do_all.c
index a3fb077..d8023e0 100644
--- a/src/decrepit/evp/evp_do_all.c
+++ b/src/decrepit/evp/evp_do_all.c
@@ -78,7 +78,6 @@
   callback(EVP_sha256(), "SHA256", NULL, arg);
   callback(EVP_sha384(), "SHA384", NULL, arg);
   callback(EVP_sha512(), "SHA512", NULL, arg);
-  callback(EVP_sha512_256(), "SHA512-256", NULL, arg);
 
   callback(EVP_md4(), "md4", NULL, arg);
   callback(EVP_md5(), "md5", NULL, arg);
@@ -87,7 +86,6 @@
   callback(EVP_sha256(), "sha256", NULL, arg);
   callback(EVP_sha384(), "sha384", NULL, arg);
   callback(EVP_sha512(), "sha512", NULL, arg);
-  callback(EVP_sha512_256(), "sha512-256", NULL, arg);
 }
 
 void EVP_MD_do_all(void (*callback)(const EVP_MD *cipher, const char *name,
diff --git a/src/include/openssl/asn1.h b/src/include/openssl/asn1.h
index 4f6fb3b..db467fd 100644
--- a/src/include/openssl/asn1.h
+++ b/src/include/openssl/asn1.h
@@ -111,6 +111,10 @@
 // V_ASN1_UNDEF is used in some APIs to indicate an ASN.1 element is omitted.
 #define V_ASN1_UNDEF (-1)
 
+// V_ASN1_APP_CHOOSE is used in some APIs to specify a default ASN.1 type based
+// on the context.
+#define V_ASN1_APP_CHOOSE (-2)
+
 // V_ASN1_OTHER is used in |ASN1_TYPE| to indicate a non-universal ASN.1 type.
 #define V_ASN1_OTHER (-3)
 
@@ -153,31 +157,6 @@
 #define V_ASN1_NEG_INTEGER (V_ASN1_INTEGER | V_ASN1_NEG)
 #define V_ASN1_NEG_ENUMERATED (V_ASN1_ENUMERATED | V_ASN1_NEG)
 
-// The following constants are bitmask representations of ASN.1 types.
-#define B_ASN1_NUMERICSTRING 0x0001
-#define B_ASN1_PRINTABLESTRING 0x0002
-#define B_ASN1_T61STRING 0x0004
-#define B_ASN1_TELETEXSTRING 0x0004
-#define B_ASN1_VIDEOTEXSTRING 0x0008
-#define B_ASN1_IA5STRING 0x0010
-#define B_ASN1_GRAPHICSTRING 0x0020
-#define B_ASN1_ISO64STRING 0x0040
-#define B_ASN1_VISIBLESTRING 0x0040
-#define B_ASN1_GENERALSTRING 0x0080
-#define B_ASN1_UNIVERSALSTRING 0x0100
-#define B_ASN1_OCTET_STRING 0x0200
-#define B_ASN1_BIT_STRING 0x0400
-#define B_ASN1_BMPSTRING 0x0800
-#define B_ASN1_UNKNOWN 0x1000
-#define B_ASN1_UTF8STRING 0x2000
-#define B_ASN1_UTCTIME 0x4000
-#define B_ASN1_GENERALIZEDTIME 0x8000
-#define B_ASN1_SEQUENCE 0x10000
-
-// ASN1_tag2str returns a string representation of |tag|, interpret as a tag
-// number for a universal type, or |V_ASN1_NEG_*|.
-OPENSSL_EXPORT const char *ASN1_tag2str(int tag);
-
 
 // Strings.
 //
@@ -253,6 +232,14 @@
 // treated as padding. This behavior is deprecated and should not be used.
 #define ASN1_STRING_FLAG_BITS_LEFT 0x08
 
+// ASN1_STRING_FLAG_MSTRING indicates that the |ASN1_STRING| is an MSTRING type,
+// which is how this library refers to a CHOICE type of several string types.
+// For example, DirectoryString as defined in RFC5280.
+//
+// TODO(davidben): This is only used in one place within the library and is easy
+// to accidentally drop. Can it be removed?
+#define ASN1_STRING_FLAG_MSTRING 0x040
+
 // ASN1_STRING_type_new returns a newly-allocated empty |ASN1_STRING| object of
 // type |type|, or NULL on error.
 OPENSSL_EXPORT ASN1_STRING *ASN1_STRING_type_new(int type);
@@ -324,52 +311,6 @@
 OPENSSL_EXPORT int ASN1_STRING_to_UTF8(unsigned char **out,
                                        const ASN1_STRING *in);
 
-// The following formats define encodings for use with functions like
-// |ASN1_mbstring_copy|.
-#define MBSTRING_FLAG 0x1000
-#define MBSTRING_UTF8 (MBSTRING_FLAG)
-// |MBSTRING_ASC| refers to Latin-1, not ASCII.
-#define MBSTRING_ASC (MBSTRING_FLAG | 1)
-#define MBSTRING_BMP (MBSTRING_FLAG | 2)
-#define MBSTRING_UNIV (MBSTRING_FLAG | 4)
-
-// DIRSTRING_TYPE contains the valid string types in an X.509 DirectoryString.
-#define DIRSTRING_TYPE                                            \
-  (B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING | \
-   B_ASN1_UTF8STRING)
-
-// PKCS9STRING_TYPE contains the valid string types in a PKCS9String.
-#define PKCS9STRING_TYPE (DIRSTRING_TYPE | B_ASN1_IA5STRING)
-
-// ASN1_mbstring_copy converts |len| bytes from |in| to an ASN.1 string. If
-// |len| is -1, |in| must be NUL-terminated and the length is determined by
-// |strlen|. |in| is decoded according to |inform|, which must be one of
-// |MBSTRING_*|. |mask| determines the set of valid output types and is a
-// bitmask containing a subset of |B_ASN1_PRINTABLESTRING|, |B_ASN1_IA5STRING|,
-// |B_ASN1_T61STRING|, |B_ASN1_BMPSTRING|, |B_ASN1_UNIVERSALSTRING|, and
-// |B_ASN1_UTF8STRING|, in that preference order. This function chooses the
-// first output type in |mask| which can represent |in|. It interprets T61String
-// as Latin-1, rather than T.61.
-//
-// If |mask| is zero, |DIRSTRING_TYPE| is used by default.
-//
-// On success, this function returns the |V_ASN1_*| constant corresponding to
-// the selected output type and, if |out| and |*out| are both non-NULL, updates
-// the object at |*out| with the result. If |out| is non-NULL and |*out| is
-// NULL, it instead sets |*out| to a newly-allocated |ASN1_STRING| containing
-// the result. If |out| is NULL, it returns the selected output type without
-// constructing an |ASN1_STRING|. On error, this function returns -1.
-OPENSSL_EXPORT int ASN1_mbstring_copy(ASN1_STRING **out, const uint8_t *in,
-                                      int len, int inform, unsigned long mask);
-
-// ASN1_mbstring_ncopy behaves like |ASN1_mbstring_copy| but returns an error if
-// the input is less than |minsize| or greater than |maxsize| codepoints long. A
-// |maxsize| value of zero is ignored. Note the sizes are measured in
-// codepoints, not output bytes.
-OPENSSL_EXPORT int ASN1_mbstring_ncopy(ASN1_STRING **out, const uint8_t *in,
-                                       int len, int inform, unsigned long mask,
-                                       long minsize, long maxsize);
-
 // TODO(davidben): Expand and document function prototypes generated in macros.
 
 
@@ -380,7 +321,7 @@
 // in several forms:
 //
 // Some BIT STRINGs represent a bitmask of named bits, such as the X.509 key
-// usage extension in RFC 5280, section 4.2.1.3. For such bit strings, DER
+// usage extension in RFC5280, section 4.2.1.3. For such bit strings, DER
 // imposes an additional restriction that trailing zero bits are removed. Some
 // functions like |ASN1_BIT_STRING_set_bit| help in maintaining this.
 //
@@ -511,135 +452,6 @@
 // TODO(davidben): Expand and document function prototypes generated in macros.
 
 
-// Time.
-//
-// GeneralizedTime and UTCTime values are represented as |ASN1_STRING|s. The
-// type field is |V_ASN1_GENERALIZEDTIME| or |V_ASN1_UTCTIME|, respectively. The
-// data field contains the DER encoding of the value. For example, the UNIX
-// epoch would be "19700101000000Z" for a GeneralizedTime and "700101000000Z"
-// for a UTCTime.
-//
-// ASN.1 does not define how to interpret UTCTime's two-digit year. RFC 5280
-// defines it as a range from 1950 to 2049 for X.509. The library uses the
-// RFC 5280 interpretation. It does not currently enforce the restrictions from
-// BER, and the additional restrictions from RFC 5280, but future versions may.
-// Callers should not rely on fractional seconds and non-UTC time zones.
-//
-// The |ASN1_TIME| typedef represents the X.509 Time type, which is a CHOICE of
-// GeneralizedTime and UTCTime, using UTCTime when the value is in range.
-
-// ASN1_UTCTIME_check returns one if |a| is a valid UTCTime and zero otherwise.
-OPENSSL_EXPORT int ASN1_UTCTIME_check(const ASN1_UTCTIME *a);
-
-// ASN1_UTCTIME_set represents |t| as a UTCTime and writes the result to |s|. It
-// returns |s| on success and NULL on error. If |s| is NULL, it returns a
-// newly-allocated |ASN1_UTCTIME| instead.
-//
-// Note this function may fail if the time is out of range for UTCTime.
-OPENSSL_EXPORT ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t);
-
-// ASN1_UTCTIME_adj adds |offset_day| days and |offset_sec| seconds to |t| and
-// writes the result to |s| as a UTCTime. It returns |s| on success and NULL on
-// error. If |s| is NULL, it returns a newly-allocated |ASN1_UTCTIME| instead.
-//
-// Note this function may fail if the time overflows or is out of range for
-// UTCTime.
-OPENSSL_EXPORT ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t,
-                                              int offset_day, long offset_sec);
-
-// ASN1_UTCTIME_set_string sets |s| to a UTCTime whose contents are a copy of
-// |str|. It returns one on success and zero on error or if |str| is not a valid
-// UTCTime.
-//
-// If |s| is NULL, this function validates |str| without copying it.
-OPENSSL_EXPORT int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str);
-
-// ASN1_UTCTIME_cmp_time_t compares |s| to |t|. It returns -1 if |s| < |t|, 0 if
-// they are equal, 1 if |s| > |t|, and -2 on error.
-OPENSSL_EXPORT int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t);
-
-// ASN1_GENERALIZEDTIME_check returns one if |a| is a valid GeneralizedTime and
-// zero otherwise.
-OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *a);
-
-// ASN1_GENERALIZEDTIME_set represents |t| as a GeneralizedTime and writes the
-// result to |s|. It returns |s| on success and NULL on error. If |s| is NULL,
-// it returns a newly-allocated |ASN1_GENERALIZEDTIME| instead.
-//
-// Note this function may fail if the time is out of range for GeneralizedTime.
-OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(
-    ASN1_GENERALIZEDTIME *s, time_t t);
-
-// ASN1_GENERALIZEDTIME_adj adds |offset_day| days and |offset_sec| seconds to
-// |t| and writes the result to |s| as a GeneralizedTime. It returns |s| on
-// success and NULL on error. If |s| is NULL, it returns a newly-allocated
-// |ASN1_GENERALIZEDTIME| instead.
-//
-// Note this function may fail if the time overflows or is out of range for
-// GeneralizedTime.
-OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(
-    ASN1_GENERALIZEDTIME *s, time_t t, int offset_day, long offset_sec);
-
-// ASN1_GENERALIZEDTIME_set_string sets |s| to a GeneralizedTime whose contents
-// are a copy of |str|. It returns one on success and zero on error or if |str|
-// is not a valid GeneralizedTime.
-//
-// If |s| is NULL, this function validates |str| without copying it.
-OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s,
-                                                   const char *str);
-
-// ASN1_TIME_diff computes |to| - |from|. On success, it sets |*out_days| to the
-// difference in days, rounded towards zero, sets |*out_seconds| to the
-// remainder, and returns one. On error, it returns zero.
-//
-// If |from| is before |to|, both outputs will be <= 0, with at least one
-// negative. If |from| is after |to|, both will be >= 0, with at least one
-// positive. If they are equal, ignoring fractional seconds, both will be zero.
-//
-// Note this function may fail on overflow, or if |from| or |to| cannot be
-// decoded.
-OPENSSL_EXPORT int ASN1_TIME_diff(int *out_days, int *out_seconds,
-                                  const ASN1_TIME *from, const ASN1_TIME *to);
-
-// ASN1_TIME_set represents |t| as a GeneralizedTime or UTCTime and writes
-// the result to |s|. As in RFC 5280, section 4.1.2.5, it uses UTCTime when the
-// time fits and GeneralizedTime otherwise. It returns |s| on success and NULL
-// on error. If |s| is NULL, it returns a newly-allocated |ASN1_TIME| instead.
-//
-// Note this function may fail if the time is out of range for GeneralizedTime.
-OPENSSL_EXPORT ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t);
-
-// ASN1_TIME_adj adds |offset_day| days and |offset_sec| seconds to
-// |t| and writes the result to |s|. As in RFC 5280, section 4.1.2.5, it uses
-// UTCTime when the time fits and GeneralizedTime otherwise. It returns |s| on
-// success and NULL on error. If |s| is NULL, it returns a newly-allocated
-// |ASN1_GENERALIZEDTIME| instead.
-//
-// Note this function may fail if the time overflows or is out of range for
-// GeneralizedTime.
-OPENSSL_EXPORT ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t, int offset_day,
-                                        long offset_sec);
-
-// ASN1_TIME_check returns one if |t| is a valid UTCTime or GeneralizedTime, and
-// zero otherwise. |t|'s type determines which check is performed. This
-// function does not enforce that UTCTime was used when possible.
-OPENSSL_EXPORT int ASN1_TIME_check(const ASN1_TIME *t);
-
-// ASN1_TIME_to_generalizedtime converts |t| to a GeneralizedTime. If |out| is
-// NULL, it returns a newly-allocated |ASN1_GENERALIZEDTIME| on success, or NULL
-// on error. If |out| is non-NULL and |*out| is NULL, it additionally sets
-// |*out| to the result. If |out| and |*out| are non-NULL, it instead updates
-// the object pointed by |*out| and returns |*out| on success or NULL on error.
-OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(
-    const ASN1_TIME *t, ASN1_GENERALIZEDTIME **out);
-
-// ASN1_TIME_set_string behaves like |ASN1_UTCTIME_set_string| if |str| is a
-// valid UTCTime, and |ASN1_GENERALIZEDTIME_set_string| if |str| is a valid
-// GeneralizedTime. If |str| is neither, it returns zero.
-OPENSSL_EXPORT int ASN1_TIME_set_string(ASN1_TIME *s, const char *str);
-
-// TODO(davidben): Expand and document function prototypes generated in macros.
-
 
 // Arbitrary elements.
 
@@ -746,103 +558,41 @@
 // the macros, document them, and move them to this section.
 
 
-// Human-readable output.
-//
-// The following functions output types in some human-readable format. These
-// functions may be used for debugging and logging. However, the output should
-// not be consumed programmatically. They may be ambiguous or lose information.
-
-// ASN1_UTCTIME_print writes a human-readable representation of |a| to |out|. It
-// returns one on success and zero on error.
-OPENSSL_EXPORT int ASN1_UTCTIME_print(BIO *out, const ASN1_UTCTIME *a);
-
-// ASN1_GENERALIZEDTIME_print writes a human-readable representation of |a| to
-// |out|. It returns one on success and zero on error.
-OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_print(BIO *out,
-                                              const ASN1_GENERALIZEDTIME *a);
-
-// ASN1_TIME_print writes a human-readable representation of |a| to |out|. It
-// returns one on success and zero on error.
-OPENSSL_EXPORT int ASN1_TIME_print(BIO *out, const ASN1_TIME *a);
-
-// ASN1_STRING_print writes a human-readable representation of |str| to |out|.
-// It returns one on success and zero on error. Unprintable characters are
-// replaced with '.'.
-OPENSSL_EXPORT int ASN1_STRING_print(BIO *out, const ASN1_STRING *str);
-
-// ASN1_STRFLGS_ESC_2253 causes characters to be escaped as in RFC 2253, section
-// 2.4.
-#define ASN1_STRFLGS_ESC_2253 1
-
-// ASN1_STRFLGS_ESC_CTRL causes all control characters to be escaped.
-#define ASN1_STRFLGS_ESC_CTRL 2
-
-// ASN1_STRFLGS_ESC_MSB causes all characters above 127 to be escaped.
-#define ASN1_STRFLGS_ESC_MSB 4
-
-// ASN1_STRFLGS_ESC_QUOTE causes the string to be surrounded by quotes, rather
-// than using backslashes, when characters are escaped. Fewer characters will
-// require escapes in this case.
-#define ASN1_STRFLGS_ESC_QUOTE 8
-
-// ASN1_STRFLGS_UTF8_CONVERT causes the string to be encoded as UTF-8, with each
-// byte in the UTF-8 encoding treated as an individual character for purposes of
-// escape sequences. If not set, each Unicode codepoint in the string is treated
-// as a character, with wide characters escaped as "\Uxxxx" or "\Wxxxxxxxx".
-// Note this can be ambiguous if |ASN1_STRFLGS_ESC_*| are all unset. In that
-// case, backslashes are not escaped, but wide characters are.
-#define ASN1_STRFLGS_UTF8_CONVERT 0x10
-
-// ASN1_STRFLGS_IGNORE_TYPE causes the string type to be ignored. The
-// |ASN1_STRING| in-memory representation will be printed directly.
-#define ASN1_STRFLGS_IGNORE_TYPE 0x20
-
-// ASN1_STRFLGS_SHOW_TYPE causes the string type to be included in the output.
-#define ASN1_STRFLGS_SHOW_TYPE 0x40
-
-// ASN1_STRFLGS_DUMP_ALL causes all strings to be printed as a hexdump, using
-// RFC 2253 hexstring notation, such as "#0123456789ABCDEF".
-#define ASN1_STRFLGS_DUMP_ALL 0x80
-
-// ASN1_STRFLGS_DUMP_UNKNOWN behaves like |ASN1_STRFLGS_DUMP_ALL| but only
-// applies to values of unknown type. If unset, unknown values will print
-// their contents as single-byte characters with escape sequences.
-#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100
-
-// ASN1_STRFLGS_DUMP_DER causes hexdumped strings (as determined by
-// |ASN1_STRFLGS_DUMP_ALL| or |ASN1_STRFLGS_DUMP_UNKNOWN|) to print the entire
-// DER element as in RFC 2253, rather than only the contents of the
-// |ASN1_STRING|.
-#define ASN1_STRFLGS_DUMP_DER 0x200
-
-// ASN1_STRFLGS_RFC2253 causes the string to be escaped as in RFC 2253,
-// additionally escaping control characters.
-#define ASN1_STRFLGS_RFC2253                                              \
-  (ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | \
-   ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_DUMP_UNKNOWN |                \
-   ASN1_STRFLGS_DUMP_DER)
-
-// ASN1_STRING_print_ex writes a human-readable representation of |str| to
-// |out|. It returns the number of bytes written on success and -1 on error. If
-// |out| is NULL, it returns the number of bytes it would have written, without
-// writing anything.
-//
-// The |flags| should be a combination of combination of |ASN1_STRFLGS_*|
-// constants. See the documentation for each flag for how it controls the
-// output. If unsure, use |ASN1_STRFLGS_RFC2253|.
-OPENSSL_EXPORT int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str,
-                                        unsigned long flags);
-
-// ASN1_STRING_print_ex_fp behaves like |ASN1_STRING_print_ex| but writes to a
-// |FILE| rather than a |BIO|.
-OPENSSL_EXPORT int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
-                                           unsigned long flags);
-
-
 // Underdocumented functions.
 //
 // The following functions are not yet documented and organized.
 
+// For use with d2i_ASN1_type_bytes()
+#define B_ASN1_NUMERICSTRING 0x0001
+#define B_ASN1_PRINTABLESTRING 0x0002
+#define B_ASN1_T61STRING 0x0004
+#define B_ASN1_TELETEXSTRING 0x0004
+#define B_ASN1_VIDEOTEXSTRING 0x0008
+#define B_ASN1_IA5STRING 0x0010
+#define B_ASN1_GRAPHICSTRING 0x0020
+#define B_ASN1_ISO64STRING 0x0040
+#define B_ASN1_VISIBLESTRING 0x0040
+#define B_ASN1_GENERALSTRING 0x0080
+#define B_ASN1_UNIVERSALSTRING 0x0100
+#define B_ASN1_OCTET_STRING 0x0200
+#define B_ASN1_BIT_STRING 0x0400
+#define B_ASN1_BMPSTRING 0x0800
+#define B_ASN1_UNKNOWN 0x1000
+#define B_ASN1_UTF8STRING 0x2000
+#define B_ASN1_UTCTIME 0x4000
+#define B_ASN1_GENERALIZEDTIME 0x8000
+#define B_ASN1_SEQUENCE 0x10000
+
+// For use with ASN1_mbstring_copy()
+#define MBSTRING_FLAG 0x1000
+#define MBSTRING_UTF8 (MBSTRING_FLAG)
+// |MBSTRING_ASC| refers to Latin-1, not ASCII. It is used with TeletexString
+// which, in turn, is treated as Latin-1 rather than T.61 by OpenSSL and most
+// other software.
+#define MBSTRING_ASC (MBSTRING_FLAG | 1)
+#define MBSTRING_BMP (MBSTRING_FLAG | 2)
+#define MBSTRING_UNIV (MBSTRING_FLAG | 4)
+
 DEFINE_STACK_OF(ASN1_OBJECT)
 
 // ASN1_ENCODING structure: this is used to save the received
@@ -865,6 +615,10 @@
 
 #define STABLE_FLAGS_MALLOC 0x01
 #define STABLE_NO_MASK 0x02
+#define DIRSTRING_TYPE                                            \
+  (B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING | \
+   B_ASN1_UTF8STRING)
+#define PKCS9STRING_TYPE (DIRSTRING_TYPE | B_ASN1_IA5STRING)
 
 typedef struct asn1_string_table_st {
   int nid;
@@ -874,6 +628,17 @@
   unsigned long flags;
 } ASN1_STRING_TABLE;
 
+// size limits: this stuff is taken straight from RFC2459
+
+#define ub_name 32768
+#define ub_common_name 64
+#define ub_locality_name 128
+#define ub_state_name 128
+#define ub_organization_name 64
+#define ub_organization_unit_name 64
+#define ub_title 64
+#define ub_email_address 128
+
 // Declarations for template structures: for full definitions
 // see asn1t.h
 typedef struct ASN1_TEMPLATE_st ASN1_TEMPLATE;
@@ -971,6 +736,75 @@
 
 #define DECLARE_ASN1_ITEM(name) extern OPENSSL_EXPORT const ASN1_ITEM name##_it;
 
+// Parameters used by ASN1_STRING_print_ex()
+
+// These determine which characters to escape:
+// RFC2253 special characters, control characters and
+// MSB set characters
+
+#define ASN1_STRFLGS_ESC_2253 1
+#define ASN1_STRFLGS_ESC_CTRL 2
+#define ASN1_STRFLGS_ESC_MSB 4
+
+
+// This flag determines how we do escaping: normally
+// RC2253 backslash only, set this to use backslash and
+// quote.
+
+#define ASN1_STRFLGS_ESC_QUOTE 8
+
+
+// These three flags are internal use only.
+
+// Character is a valid PrintableString character
+#define CHARTYPE_PRINTABLESTRING 0x10
+// Character needs escaping if it is the first character
+#define CHARTYPE_FIRST_ESC_2253 0x20
+// Character needs escaping if it is the last character
+#define CHARTYPE_LAST_ESC_2253 0x40
+
+// NB the internal flags are safely reused below by flags
+// handled at the top level.
+
+// If this is set we convert all character strings
+// to UTF8 first
+
+#define ASN1_STRFLGS_UTF8_CONVERT 0x10
+
+// If this is set we don't attempt to interpret content:
+// just assume all strings are 1 byte per character. This
+// will produce some pretty odd looking output!
+
+#define ASN1_STRFLGS_IGNORE_TYPE 0x20
+
+// If this is set we include the string type in the output
+#define ASN1_STRFLGS_SHOW_TYPE 0x40
+
+// This determines which strings to display and which to
+// 'dump' (hex dump of content octets or DER encoding). We can
+// only dump non character strings or everything. If we
+// don't dump 'unknown' they are interpreted as character
+// strings with 1 octet per character and are subject to
+// the usual escaping options.
+
+#define ASN1_STRFLGS_DUMP_ALL 0x80
+#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100
+
+// These determine what 'dumping' does, we can dump the
+// content octets or the DER encoding: both use the
+// RFC2253 #XXXXX notation.
+
+#define ASN1_STRFLGS_DUMP_DER 0x200
+
+// All the string flags consistent with RFC2253,
+// escaping control characters isn't essential in
+// RFC2253 but it is advisable anyway.
+
+#define ASN1_STRFLGS_RFC2253                                              \
+  (ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | \
+   ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_DUMP_UNKNOWN |                \
+   ASN1_STRFLGS_DUMP_DER)
+
 DEFINE_STACK_OF(ASN1_INTEGER)
 
 DEFINE_STACK_OF(ASN1_TYPE)
@@ -980,6 +814,20 @@
 DECLARE_ASN1_ENCODE_FUNCTIONS_const(ASN1_SEQUENCE_ANY, ASN1_SEQUENCE_ANY)
 DECLARE_ASN1_ENCODE_FUNCTIONS_const(ASN1_SEQUENCE_ANY, ASN1_SET_ANY)
 
+struct X509_algor_st {
+  ASN1_OBJECT *algorithm;
+  ASN1_TYPE *parameter;
+} /* X509_ALGOR */;
+
+DECLARE_ASN1_FUNCTIONS(X509_ALGOR)
+
+// This is used to contain a list of bit names
+typedef struct BIT_STRING_BITNAME_st {
+  int bitnum;
+  const char *lname;
+  const char *sname;
+} BIT_STRING_BITNAME;
+
 // M_ASN1_* are legacy aliases for various |ASN1_STRING| functions. Use the
 // functions themselves.
 #define M_ASN1_STRING_length(x) ASN1_STRING_length(x)
@@ -1076,6 +924,26 @@
 
 DECLARE_ASN1_FUNCTIONS(ASN1_ENUMERATED)
 
+OPENSSL_EXPORT int ASN1_UTCTIME_check(const ASN1_UTCTIME *a);
+OPENSSL_EXPORT ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t);
+OPENSSL_EXPORT ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t,
+                                              int offset_day, long offset_sec);
+OPENSSL_EXPORT int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str);
+OPENSSL_EXPORT int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t);
+#if 0
+time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s);
+#endif
+
+OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *a);
+OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(
+    ASN1_GENERALIZEDTIME *s, time_t t);
+OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(
+    ASN1_GENERALIZEDTIME *s, time_t t, int offset_day, long offset_sec);
+OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s,
+                                                   const char *str);
+OPENSSL_EXPORT int ASN1_TIME_diff(int *pday, int *psec, const ASN1_TIME *from,
+                                  const ASN1_TIME *to);
+
 DECLARE_ASN1_FUNCTIONS(ASN1_OCTET_STRING)
 OPENSSL_EXPORT ASN1_OCTET_STRING *ASN1_OCTET_STRING_dup(
     const ASN1_OCTET_STRING *a);
@@ -1102,6 +970,14 @@
 DECLARE_ASN1_FUNCTIONS(ASN1_GENERALIZEDTIME)
 DECLARE_ASN1_FUNCTIONS(ASN1_TIME)
 
+OPENSSL_EXPORT ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t);
+OPENSSL_EXPORT ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t, int offset_day,
+                                        long offset_sec);
+OPENSSL_EXPORT int ASN1_TIME_check(const ASN1_TIME *t);
+OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(
+    const ASN1_TIME *t, ASN1_GENERALIZEDTIME **out);
+OPENSSL_EXPORT int ASN1_TIME_set_string(ASN1_TIME *s, const char *str);
+
 OPENSSL_EXPORT int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a);
 OPENSSL_EXPORT int i2a_ASN1_ENUMERATED(BIO *bp, const ASN1_ENUMERATED *a);
 OPENSSL_EXPORT int i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *a);
@@ -1114,11 +990,9 @@
                                                int len, const char *sn,
                                                const char *ln);
 
-// ASN1_PRINTABLE_type interprets |len| bytes from |s| as a Latin-1 string. It
-// returns the first of |V_ASN1_PRINTABLESTRING|, |V_ASN1_IA5STRING|, or
-// |V_ASN1_T61STRING| that can represent every character. If |len| is negative,
-// |strlen(s)| is used instead.
-OPENSSL_EXPORT int ASN1_PRINTABLE_type(const unsigned char *s, int len);
+// General
+// given a string, return the correct type, max is the maximum length
+OPENSSL_EXPORT int ASN1_PRINTABLE_type(const unsigned char *s, int max);
 
 OPENSSL_EXPORT unsigned long ASN1_tag2bit(int tag);
 
@@ -1132,11 +1006,23 @@
 
 OPENSSL_EXPORT void *ASN1_item_dup(const ASN1_ITEM *it, void *x);
 
+#ifndef OPENSSL_NO_FP_API
 OPENSSL_EXPORT void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x);
 OPENSSL_EXPORT int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x);
+OPENSSL_EXPORT int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
+                                           unsigned long flags);
+#endif
 
 OPENSSL_EXPORT void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x);
 OPENSSL_EXPORT int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x);
+OPENSSL_EXPORT int ASN1_UTCTIME_print(BIO *fp, const ASN1_UTCTIME *a);
+OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_print(BIO *fp,
+                                              const ASN1_GENERALIZEDTIME *a);
+OPENSSL_EXPORT int ASN1_TIME_print(BIO *fp, const ASN1_TIME *a);
+OPENSSL_EXPORT int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v);
+OPENSSL_EXPORT int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str,
+                                        unsigned long flags);
+OPENSSL_EXPORT const char *ASN1_tag2str(int tag);
 
 // Used to load and write netscape format cert
 
@@ -1146,14 +1032,16 @@
 OPENSSL_EXPORT ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it,
                                            ASN1_OCTET_STRING **oct);
 
-// ASN1_STRING_set_default_mask does nothing.
 OPENSSL_EXPORT void ASN1_STRING_set_default_mask(unsigned long mask);
-
-// ASN1_STRING_set_default_mask_asc returns one.
 OPENSSL_EXPORT int ASN1_STRING_set_default_mask_asc(const char *p);
-
-// ASN1_STRING_get_default_mask returns |B_ASN1_UTF8STRING|.
 OPENSSL_EXPORT unsigned long ASN1_STRING_get_default_mask(void);
+OPENSSL_EXPORT int ASN1_mbstring_copy(ASN1_STRING **out,
+                                      const unsigned char *in, int len,
+                                      int inform, unsigned long mask);
+OPENSSL_EXPORT int ASN1_mbstring_ncopy(ASN1_STRING **out,
+                                       const unsigned char *in, int len,
+                                       int inform, unsigned long mask,
+                                       long minsize, long maxsize);
 
 OPENSSL_EXPORT ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out,
                                                    const unsigned char *in,
diff --git a/src/include/openssl/asn1t.h b/src/include/openssl/asn1t.h
index 45302ef..c5e2685 100644
--- a/src/include/openssl/asn1t.h
+++ b/src/include/openssl/asn1t.h
@@ -389,6 +389,13 @@
 /* Field is a SEQUENCE OF */
 #define ASN1_TFLG_SEQUENCE_OF	(0x2 << 1)
 
+/* Special case: this refers to a SET OF that
+ * will be sorted into DER order when encoded *and*
+ * the corresponding STACK will be modified to match
+ * the new order.
+ */
+#define ASN1_TFLG_SET_ORDER	(0x3 << 1)
+
 /* Mask for SET OF or SEQUENCE OF */
 #define ASN1_TFLG_SK_MASK	(0x3 << 1)
 
@@ -595,8 +602,8 @@
 #define ASN1_OP_FREE_POST	3
 #define ASN1_OP_D2I_PRE		4
 #define ASN1_OP_D2I_POST	5
-/* ASN1_OP_I2D_PRE and ASN1_OP_I2D_POST are not supported. We leave the
- * constants undefined so code relying on them does not accidentally compile. */
+#define ASN1_OP_I2D_PRE		6
+#define ASN1_OP_I2D_POST	7
 #define ASN1_OP_PRINT_PRE	8
 #define ASN1_OP_PRINT_POST	9
 #define ASN1_OP_STREAM_PRE	10
diff --git a/src/include/openssl/base.h b/src/include/openssl/base.h
index 8be10d1..ea4366a 100644
--- a/src/include/openssl/base.h
+++ b/src/include/openssl/base.h
@@ -145,7 +145,7 @@
 // Trusty isn't Linux but currently defines __linux__. As a workaround, we
 // exclude it here.
 // TODO(b/169780122): Remove this workaround once Trusty no longer defines it.
-#if defined(__linux__) && !defined(__TRUSTY__)
+#if defined(__linux__) && !defined(TRUSTY)
 #define OPENSSL_LINUX
 #endif
 
@@ -153,7 +153,7 @@
 #define OPENSSL_FUCHSIA
 #endif
 
-#if defined(__TRUSTY__)
+#if defined(TRUSTY)
 #define OPENSSL_TRUSTY
 #define OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED
 #endif
@@ -422,7 +422,6 @@
 typedef struct rand_meth_st RAND_METHOD;
 typedef struct rc4_key_st RC4_KEY;
 typedef struct rsa_meth_st RSA_METHOD;
-typedef struct rsa_pss_params_st RSA_PSS_PARAMS;
 typedef struct rsa_st RSA;
 typedef struct sha256_state_st SHA256_CTX;
 typedef struct sha512_state_st SHA512_CTX;
@@ -446,10 +445,9 @@
 typedef struct v3_ext_ctx X509V3_CTX;
 typedef struct x509_attributes_st X509_ATTRIBUTE;
 typedef struct x509_cert_aux_st X509_CERT_AUX;
+typedef struct x509_cinf_st X509_CINF;
 typedef struct x509_crl_method_st X509_CRL_METHOD;
 typedef struct x509_lookup_st X509_LOOKUP;
-typedef struct x509_lookup_method_st X509_LOOKUP_METHOD;
-typedef struct x509_object_st X509_OBJECT;
 typedef struct x509_revoked_st X509_REVOKED;
 typedef struct x509_st X509;
 typedef struct x509_store_ctx_st X509_STORE_CTX;
diff --git a/src/include/openssl/bio.h b/src/include/openssl/bio.h
index 18bc893..f25492a 100644
--- a/src/include/openssl/bio.h
+++ b/src/include/openssl/bio.h
@@ -377,9 +377,7 @@
 OPENSSL_EXPORT const BIO_METHOD *BIO_s_mem(void);
 
 // BIO_new_mem_buf creates read-only BIO that reads from |len| bytes at |buf|.
-// It returns the BIO or NULL on error. This function does not copy or take
-// ownership of |buf|. The caller must ensure the memory pointed to by |buf|
-// outlives the |BIO|.
+// It does not take ownership of |buf|. It returns the BIO or NULL on error.
 //
 // If |len| is negative, then |buf| is treated as a NUL-terminated string, but
 // don't depend on this in new code.
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index 5ca8b85..295ca62 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -687,9 +687,9 @@
 // BN_prime_checks_for_validation can be used as the |checks| argument to the
 // primarily testing functions when validating an externally-supplied candidate
 // prime. It gives a false positive rate of at most 2^{-128}. (The worst case
-// false positive rate for a single iteration is 1/4 per
-// https://eprint.iacr.org/2018/749. (1/4)^64 = 2^{-128}.)
-#define BN_prime_checks_for_validation 64
+// false positive rate for a single iteration is 1/4, so we perform 32
+// iterations.)
+#define BN_prime_checks_for_validation 32
 
 // BN_prime_checks_for_generation can be used as the |checks| argument to the
 // primality testing functions when generating random primes. It gives a false
diff --git a/src/include/openssl/bytestring.h b/src/include/openssl/bytestring.h
index 5ef3742..39b7fb2 100644
--- a/src/include/openssl/bytestring.h
+++ b/src/include/openssl/bytestring.h
@@ -154,11 +154,6 @@
 // returns one on success and zero on error.
 OPENSSL_EXPORT int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out);
 
-// CBS_get_until_first finds the first instance of |c| in |cbs|. If found, it
-// sets |*out| to the text before the match, advances |cbs| over it, and returns
-// one. Otherwise, it returns zero and leaves |cbs| unmodified.
-OPENSSL_EXPORT int CBS_get_until_first(CBS *cbs, CBS *out, uint8_t c);
-
 
 // Parsing ASN.1
 //
@@ -468,10 +463,6 @@
 // success and zero otherwise.
 OPENSSL_EXPORT int CBB_add_bytes(CBB *cbb, const uint8_t *data, size_t len);
 
-// CBB_add_zeros append |len| bytes with value zero to |cbb|. It returns one on
-// success and zero otherwise.
-OPENSSL_EXPORT int CBB_add_zeros(CBB *cbb, size_t len);
-
 // CBB_add_space appends |len| bytes to |cbb| and sets |*out_data| to point to
 // the beginning of that space. The caller must then write |len| bytes of
 // actual contents to |*out_data|. It returns one on success and zero
diff --git a/src/include/openssl/hkdf.h b/src/include/openssl/hkdf.h
index 5b27acc..59aaa49 100644
--- a/src/include/openssl/hkdf.h
+++ b/src/include/openssl/hkdf.h
@@ -41,10 +41,6 @@
 // keying material |secret| and salt |salt| using |digest|, and outputs
 // |out_len| bytes to |out_key|. The maximum output size is |EVP_MAX_MD_SIZE|.
 // It returns one on success and zero on error.
-//
-// WARNING: This function orders the inputs differently from RFC 5869
-// specification. Double-check which parameter is the secret/IKM and which is
-// the salt when using.
 OPENSSL_EXPORT int HKDF_extract(uint8_t *out_key, size_t *out_len,
                                 const EVP_MD *digest, const uint8_t *secret,
                                 size_t secret_len, const uint8_t *salt,
diff --git a/src/include/openssl/pem.h b/src/include/openssl/pem.h
index a94f276..f39989e 100644
--- a/src/include/openssl/pem.h
+++ b/src/include/openssl/pem.h
@@ -112,6 +112,15 @@
 // write. Now they are all implemented with either:
 // IMPLEMENT_PEM_rw(...) or IMPLEMENT_PEM_rw_cb(...)
 
+#ifdef OPENSSL_NO_FP_API
+
+#define IMPLEMENT_PEM_read_fp(name, type, str, asn1)            //
+#define IMPLEMENT_PEM_write_fp(name, type, str, asn1)           //
+#define IMPLEMENT_PEM_write_fp_const(name, type, str, asn1)     //
+#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1)        //
+#define IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1)  //
+
+#else
 
 #define IMPLEMENT_PEM_read_fp(name, type, str, asn1)                         \
   static void *pem_read_##name##_d2i(void **x, const unsigned char **inp,    \
@@ -164,6 +173,7 @@
                           cb, u);                                              \
   }
 
+#endif
 
 #define IMPLEMENT_PEM_read_bio(name, type, str, asn1)                         \
   static void *pem_read_bio_##name##_d2i(void **x, const unsigned char **inp, \
@@ -250,6 +260,14 @@
 
 // These are the same except they are for the declarations
 
+#if defined(OPENSSL_NO_FP_API)
+
+#define DECLARE_PEM_read_fp(name, type)      //
+#define DECLARE_PEM_write_fp(name, type)     //
+#define DECLARE_PEM_write_cb_fp(name, type)  //
+
+#else
+
 #define DECLARE_PEM_read_fp(name, type)                    \
   OPENSSL_EXPORT type *PEM_read_##name(FILE *fp, type **x, \
                                        pem_password_cb *cb, void *u);
@@ -265,6 +283,8 @@
       FILE *fp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen, \
       pem_password_cb *cb, void *u);
 
+#endif
+
 #define DECLARE_PEM_read_bio(name, type)                      \
   OPENSSL_EXPORT type *PEM_read_bio_##name(BIO *bp, type **x, \
                                            pem_password_cb *cb, void *u);
diff --git a/src/include/openssl/pkcs7.h b/src/include/openssl/pkcs7.h
index 77e13d7..8f2a885 100644
--- a/src/include/openssl/pkcs7.h
+++ b/src/include/openssl/pkcs7.h
@@ -200,22 +200,15 @@
 #define PKCS7_STREAM 0x1000
 #define PKCS7_PARTIAL 0x4000
 
-// PKCS7_sign can operate in two modes to provide some backwards compatibility:
-//
-// The first mode assembles |certs| into a PKCS#7 signed data ContentInfo with
+// PKCS7_sign assembles |certs| into a PKCS#7 signed data ContentInfo with
 // external data and no signatures. It returns a newly-allocated |PKCS7| on
 // success or NULL on error. |sign_cert| and |pkey| must be NULL. |data| is
-// ignored. |flags| must be equal to |PKCS7_DETACHED|. Additionally,
-// certificates in SignedData structures are unordered. The order of |certs|
-// will not be preserved.
-//
-// The second mode generates a detached RSA SHA-256 signature of |data| using
-// |pkey| and produces a PKCS#7 SignedData structure containing it. |certs|
-// must be NULL and |flags| must be exactly |PKCS7_NOATTR | PKCS7_BINARY |
-// PKCS7_NOCERTS | PKCS7_DETACHED|.
+// ignored. |flags| must be equal to |PKCS7_DETACHED|.
 //
 // Note this function only implements a subset of the corresponding OpenSSL
-// function. It is provided for backwards compatibility only.
+// function. It is provided for backwards compatibility only. Additionally,
+// certificates in SignedData structures are unordered. The order of |certs|
+// will not be preserved.
 OPENSSL_EXPORT PKCS7 *PKCS7_sign(X509 *sign_cert, EVP_PKEY *pkey,
                                  STACK_OF(X509) *certs, BIO *data, int flags);
 
diff --git a/src/include/openssl/rsa.h b/src/include/openssl/rsa.h
index 95a478a..27bc7bf 100644
--- a/src/include/openssl/rsa.h
+++ b/src/include/openssl/rsa.h
@@ -684,11 +684,6 @@
 // on success or zero otherwise.
 OPENSSL_EXPORT int RSA_print(BIO *bio, const RSA *rsa, int indent);
 
-// RSA_get0_pss_params returns NULL. In OpenSSL, this function retries RSA-PSS
-// parameters associated with |RSA| objects, but BoringSSL does not support
-// the id-RSASSA-PSS key encoding.
-OPENSSL_EXPORT const RSA_PSS_PARAMS *RSA_get0_pss_params(const RSA *rsa);
-
 
 struct rsa_meth_st {
   struct openssl_method_common_st common;
diff --git a/src/include/openssl/span.h b/src/include/openssl/span.h
index 79f1d41..7410bf9 100644
--- a/src/include/openssl/span.h
+++ b/src/include/openssl/span.h
@@ -94,6 +94,18 @@
 template <typename T>
 class Span : private internal::SpanBase<const T> {
  private:
+  // Heuristically test whether C is a container type that can be converted into
+  // a Span by checking for data() and size() member functions.
+  //
+  // TODO(davidben): Switch everything to std::enable_if_t when we remove
+  // support for MSVC 2015. Although we could write our own enable_if_t and MSVC
+  // 2015 has std::enable_if_t anyway, MSVC 2015's SFINAE implementation is
+  // problematic and does not work below unless we write the ::type at use.
+  template <typename C>
+  using EnableIfContainer = std::enable_if<
+      std::is_convertible<decltype(std::declval<C>().data()), T *>::value &&
+      std::is_integral<decltype(std::declval<C>().size())>::value>;
+
   static const size_t npos = static_cast<size_t>(-1);
 
  public:
@@ -104,27 +116,12 @@
   constexpr Span(T (&array)[N]) : Span(array, N) {}
 
   template <
-      typename C,
-      // TODO(davidben): Switch everything to std::enable_if_t when we remove
-      // support for MSVC 2015. Although we could write our own enable_if_t and
-      // MSVC 2015 has std::enable_if_t anyway, MSVC 2015's SFINAE
-      // implementation is problematic and does not work below unless we write
-      // the ::type at use.
-      //
-      // TODO(davidben): Move this and the identical copy below into an
-      // EnableIfContainer alias when we drop MSVC 2015 support. MSVC 2015's
-      // SFINAE support cannot handle type aliases.
-      typename = typename std::enable_if<
-          std::is_convertible<decltype(std::declval<C>().data()), T *>::value &&
-          std::is_integral<decltype(std::declval<C>().size())>::value>::type,
+      typename C, typename = typename EnableIfContainer<C>::type,
       typename = typename std::enable_if<std::is_const<T>::value, C>::type>
   Span(const C &container) : data_(container.data()), size_(container.size()) {}
 
   template <
-      typename C,
-      typename = typename std::enable_if<
-          std::is_convertible<decltype(std::declval<C>().data()), T *>::value &&
-          std::is_integral<decltype(std::declval<C>().size())>::value>::type,
+      typename C, typename = typename EnableIfContainer<C>::type,
       typename = typename std::enable_if<!std::is_const<T>::value, C>::type>
   explicit Span(C &container)
       : data_(container.data()), size_(container.size()) {}
@@ -161,30 +158,11 @@
 
   Span subspan(size_t pos = 0, size_t len = npos) const {
     if (pos > size_) {
-      // absl::Span throws an exception here. Note std::span and Chromium
-      // base::span additionally forbid pos + len being out of range, with a
-      // special case at npos/dynamic_extent, while absl::Span::subspan clips
-      // the span. For now, we align with absl::Span in case we switch to it in
-      // the future.
-      abort();
+      abort();  // absl::Span throws an exception here.
     }
     return Span(data_ + pos, std::min(size_ - pos, len));
   }
 
-  Span first(size_t len) {
-    if (len > size_) {
-      abort();
-    }
-    return Span(data_, len);
-  }
-
-  Span last(size_t len) {
-    if (len > size_) {
-      abort();
-    }
-    return Span(data_ + size_ - len, len);
-  }
-
  private:
   T *data_;
   size_t size_;
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 5965cb4..82e1da8 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -1649,11 +1649,6 @@
 // abbreviated handshake. It is reference-counted and immutable. Once
 // established, an |SSL_SESSION| may be shared by multiple |SSL| objects on
 // different threads and must not be modified.
-//
-// Note the TLS notion of "session" is not suitable for application-level
-// session state. It is an optional caching mechanism for the handshake. Not all
-// connections within an application-level session will reuse TLS sessions. TLS
-// sessions may be dropped by the client or ignored by the server at any time.
 
 DECLARE_PEM_rw(SSL_SESSION, SSL_SESSION)
 
@@ -1708,19 +1703,6 @@
 
 // SSL_SESSION_get_id returns a pointer to a buffer containing |session|'s
 // session ID and sets |*out_len| to its length.
-//
-// This function should only be used for implementing a TLS session cache. TLS
-// sessions are not suitable for application-level session state, and a session
-// ID is an implementation detail of the TLS resumption handshake mechanism. Not
-// all resumption flows use session IDs, and not all connections within an
-// application-level session will reuse TLS sessions.
-//
-// To determine if resumption occurred, use |SSL_session_reused| instead.
-// Comparing session IDs will not give the right result in all cases.
-//
-// As a workaround for some broken applications, BoringSSL sometimes synthesizes
-// arbitrary session IDs for non-ID-based sessions. This behavior may be
-// removed in the future.
 OPENSSL_EXPORT const uint8_t *SSL_SESSION_get_id(const SSL_SESSION *session,
                                                  unsigned *out_len);
 
@@ -3587,7 +3569,7 @@
 //
 // ECH support in BoringSSL is still experimental and under development.
 //
-// See https://tools.ietf.org/html/draft-ietf-tls-esni-13.
+// See https://tools.ietf.org/html/draft-ietf-tls-esni-10.
 
 // SSL_set_enable_ech_grease configures whether the client will send a GREASE
 // ECH extension when no supported ECHConfig is available.
@@ -3619,12 +3601,12 @@
                                             const uint8_t *ech_config_list,
                                             size_t ech_config_list_len);
 
-// SSL_get0_ech_name_override, if |ssl| is a client and the server rejected ECH,
-// sets |*out_name| and |*out_name_len| to point to a buffer containing the ECH
-// public name. Otherwise, the buffer will be empty.
+// SSL_get0_ech_name_override sets |*out_name| and |*out_name_len| to point to a
+// buffer containing the ECH public name, if the server rejected ECH, or the
+// empty string otherwise.
 //
-// When offering ECH as a client, this function should be called during the
-// certificate verification callback (see |SSL_CTX_set_custom_verify|). If
+// This function should be called during the certificate verification callback
+// (see |SSL_CTX_set_custom_verify|) if |ssl| is a client offering ECH. If
 // |*out_name_len| is non-zero, the caller should verify the certificate against
 // the result, interpreted as a DNS name, rather than the true server name. In
 // this case, the handshake will never succeed and is only used to authenticate
@@ -4912,6 +4894,12 @@
 OPENSSL_EXPORT int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *out,
                                                       const char *dir);
 
+// SSL_set_verify_result calls |abort| unless |result| is |X509_V_OK|.
+//
+// TODO(davidben): Remove this function once it has been removed from
+// netty-tcnative.
+OPENSSL_EXPORT void SSL_set_verify_result(SSL *ssl, long result);
+
 // SSL_CTX_enable_tls_channel_id calls |SSL_CTX_set_tls_channel_id_enabled|.
 OPENSSL_EXPORT int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx);
 
@@ -5566,8 +5554,6 @@
 #define SSL_R_INVALID_ECH_PUBLIC_NAME 317
 #define SSL_R_INVALID_ECH_CONFIG_LIST 318
 #define SSL_R_ECH_REJECTED 319
-#define SSL_R_OUTER_EXTENSION_NOT_FOUND 320
-#define SSL_R_INCONSISTENT_ECH_NEGOTIATION 321
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/src/include/openssl/tls1.h b/src/include/openssl/tls1.h
index a3136c0..2886e2c 100644
--- a/src/include/openssl/tls1.h
+++ b/src/include/openssl/tls1.h
@@ -179,28 +179,28 @@
 #define TLS1_AD_UNKNOWN_PSK_IDENTITY 115
 #define TLS1_AD_CERTIFICATE_REQUIRED 116
 #define TLS1_AD_NO_APPLICATION_PROTOCOL 120
-#define TLS1_AD_ECH_REQUIRED 121  // draft-ietf-tls-esni-13
+#define TLS1_AD_ECH_REQUIRED 121  // draft-ietf-tls-esni-10
 
-// ExtensionType values from RFC 6066
+// ExtensionType values from RFC6066
 #define TLSEXT_TYPE_server_name 0
 #define TLSEXT_TYPE_status_request 5
 
-// ExtensionType values from RFC 4492
+// ExtensionType values from RFC4492
 #define TLSEXT_TYPE_ec_point_formats 11
 
-// ExtensionType values from RFC 5246
+// ExtensionType values from RFC5246
 #define TLSEXT_TYPE_signature_algorithms 13
 
-// ExtensionType value from RFC 5764
+// ExtensionType value from RFC5764
 #define TLSEXT_TYPE_srtp 14
 
-// ExtensionType value from RFC 7301
+// ExtensionType value from RFC7301
 #define TLSEXT_TYPE_application_layer_protocol_negotiation 16
 
-// ExtensionType value from RFC 7685
+// ExtensionType value from RFC7685
 #define TLSEXT_TYPE_padding 21
 
-// ExtensionType value from RFC 7627
+// ExtensionType value from RFC7627
 #define TLSEXT_TYPE_extended_master_secret 23
 
 // ExtensionType value from draft-ietf-quic-tls. Drafts 00 through 32 use
@@ -210,7 +210,7 @@
 // use the value 57 which was officially registered with IANA.
 #define TLSEXT_TYPE_quic_transport_parameters_legacy 0xffa5
 
-// ExtensionType value from RFC 9000
+// ExtensionType value from RFC9000
 #define TLSEXT_TYPE_quic_transport_parameters 57
 
 // TLSEXT_TYPE_quic_transport_parameters_standard is an alias for
@@ -219,13 +219,13 @@
 #define TLSEXT_TYPE_quic_transport_parameters_standard \
   TLSEXT_TYPE_quic_transport_parameters
 
-// ExtensionType value from RFC 8879
+// ExtensionType value from RFC8879
 #define TLSEXT_TYPE_cert_compression 27
 
-// ExtensionType value from RFC 4507
+// ExtensionType value from RFC4507
 #define TLSEXT_TYPE_session_ticket 35
 
-// ExtensionType values from RFC 8446
+// ExtensionType values from RFC8446
 #define TLSEXT_TYPE_supported_groups 10
 #define TLSEXT_TYPE_pre_shared_key 41
 #define TLSEXT_TYPE_early_data 42
@@ -236,7 +236,7 @@
 #define TLSEXT_TYPE_signature_algorithms_cert 50
 #define TLSEXT_TYPE_key_share 51
 
-// ExtensionType value from RFC 5746
+// ExtensionType value from RFC5746
 #define TLSEXT_TYPE_renegotiate 0xff01
 
 // ExtensionType value from draft-ietf-tls-subcerts.
@@ -246,12 +246,13 @@
 // extension number.
 #define TLSEXT_TYPE_application_settings 17513
 
-// ExtensionType values from draft-ietf-tls-esni-13. This is not an IANA defined
+// ExtensionType values from draft-ietf-tls-esni-10. This is not an IANA defined
 // extension number.
-#define TLSEXT_TYPE_encrypted_client_hello 0xfe0d
+#define TLSEXT_TYPE_encrypted_client_hello 0xfe0a
+#define TLSEXT_TYPE_ech_is_inner 0xda09
 #define TLSEXT_TYPE_ech_outer_extensions 0xfd00
 
-// ExtensionType value from RFC 6962
+// ExtensionType value from RFC6962
 #define TLSEXT_TYPE_certificate_timestamp 18
 
 // This is not an IANA defined extension number
@@ -312,7 +313,7 @@
 #define TLS1_CK_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA 0x03000065
 #define TLS1_CK_DHE_DSS_WITH_RC4_128_SHA 0x03000066
 
-// AES ciphersuites from RFC 3268
+// AES ciphersuites from RFC3268
 
 #define TLS1_CK_RSA_WITH_AES_128_SHA 0x0300002F
 #define TLS1_CK_DH_DSS_WITH_AES_128_SHA 0x03000030
@@ -336,7 +337,7 @@
 #define TLS1_CK_DH_RSA_WITH_AES_128_SHA256 0x0300003F
 #define TLS1_CK_DHE_DSS_WITH_AES_128_SHA256 0x03000040
 
-// Camellia ciphersuites from RFC 4132
+// Camellia ciphersuites from RFC4132
 #define TLS1_CK_RSA_WITH_CAMELLIA_128_CBC_SHA 0x03000041
 #define TLS1_CK_DH_DSS_WITH_CAMELLIA_128_CBC_SHA 0x03000042
 #define TLS1_CK_DH_RSA_WITH_CAMELLIA_128_CBC_SHA 0x03000043
@@ -353,7 +354,7 @@
 #define TLS1_CK_ADH_WITH_AES_128_SHA256 0x0300006C
 #define TLS1_CK_ADH_WITH_AES_256_SHA256 0x0300006D
 
-// Camellia ciphersuites from RFC 4132
+// Camellia ciphersuites from RFC4132
 #define TLS1_CK_RSA_WITH_CAMELLIA_256_CBC_SHA 0x03000084
 #define TLS1_CK_DH_DSS_WITH_CAMELLIA_256_CBC_SHA 0x03000085
 #define TLS1_CK_DH_RSA_WITH_CAMELLIA_256_CBC_SHA 0x03000086
@@ -361,7 +362,7 @@
 #define TLS1_CK_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA 0x03000088
 #define TLS1_CK_ADH_WITH_CAMELLIA_256_CBC_SHA 0x03000089
 
-// SEED ciphersuites from RFC 4162
+// SEED ciphersuites from RFC4162
 #define TLS1_CK_RSA_WITH_SEED_SHA 0x03000096
 #define TLS1_CK_DH_DSS_WITH_SEED_SHA 0x03000097
 #define TLS1_CK_DH_RSA_WITH_SEED_SHA 0x03000098
@@ -369,7 +370,7 @@
 #define TLS1_CK_DHE_RSA_WITH_SEED_SHA 0x0300009A
 #define TLS1_CK_ADH_WITH_SEED_SHA 0x0300009B
 
-// TLS v1.2 GCM ciphersuites from RFC 5288
+// TLS v1.2 GCM ciphersuites from RFC5288
 #define TLS1_CK_RSA_WITH_AES_128_GCM_SHA256 0x0300009C
 #define TLS1_CK_RSA_WITH_AES_256_GCM_SHA384 0x0300009D
 #define TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256 0x0300009E
@@ -383,7 +384,7 @@
 #define TLS1_CK_ADH_WITH_AES_128_GCM_SHA256 0x030000A6
 #define TLS1_CK_ADH_WITH_AES_256_GCM_SHA384 0x030000A7
 
-// ECC ciphersuites from RFC 4492
+// ECC ciphersuites from RFC4492
 #define TLS1_CK_ECDH_ECDSA_WITH_NULL_SHA 0x0300C001
 #define TLS1_CK_ECDH_ECDSA_WITH_RC4_128_SHA 0x0300C002
 #define TLS1_CK_ECDH_ECDSA_WITH_DES_192_CBC3_SHA 0x0300C003
@@ -425,7 +426,7 @@
 #define TLS1_CK_SRP_SHA_RSA_WITH_AES_256_CBC_SHA 0x0300C021
 #define TLS1_CK_SRP_SHA_DSS_WITH_AES_256_CBC_SHA 0x0300C022
 
-// ECDH HMAC based ciphersuites from RFC 5289
+// ECDH HMAC based ciphersuites from RFC5289
 
 #define TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256 0x0300C023
 #define TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384 0x0300C024
@@ -436,7 +437,7 @@
 #define TLS1_CK_ECDH_RSA_WITH_AES_128_SHA256 0x0300C029
 #define TLS1_CK_ECDH_RSA_WITH_AES_256_SHA384 0x0300C02A
 
-// ECDH GCM based ciphersuites from RFC 5289
+// ECDH GCM based ciphersuites from RFC5289
 #define TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0x0300C02B
 #define TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0x0300C02C
 #define TLS1_CK_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0x0300C02D
@@ -472,7 +473,7 @@
 #define TLS1_TXT_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA "EXP1024-DHE-DSS-RC4-SHA"
 #define TLS1_TXT_DHE_DSS_WITH_RC4_128_SHA "DHE-DSS-RC4-SHA"
 
-// AES ciphersuites from RFC 3268
+// AES ciphersuites from RFC3268
 #define TLS1_TXT_RSA_WITH_AES_128_SHA "AES128-SHA"
 #define TLS1_TXT_DH_DSS_WITH_AES_128_SHA "DH-DSS-AES128-SHA"
 #define TLS1_TXT_DH_RSA_WITH_AES_128_SHA "DH-RSA-AES128-SHA"
@@ -487,7 +488,7 @@
 #define TLS1_TXT_DHE_RSA_WITH_AES_256_SHA "DHE-RSA-AES256-SHA"
 #define TLS1_TXT_ADH_WITH_AES_256_SHA "ADH-AES256-SHA"
 
-// ECC ciphersuites from RFC 4492
+// ECC ciphersuites from RFC4492
 #define TLS1_TXT_ECDH_ECDSA_WITH_NULL_SHA "ECDH-ECDSA-NULL-SHA"
 #define TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA "ECDH-ECDSA-RC4-SHA"
 #define TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA "ECDH-ECDSA-DES-CBC3-SHA"
@@ -539,7 +540,7 @@
 #define TLS1_TXT_SRP_SHA_RSA_WITH_AES_256_CBC_SHA "SRP-RSA-AES-256-CBC-SHA"
 #define TLS1_TXT_SRP_SHA_DSS_WITH_AES_256_CBC_SHA "SRP-DSS-AES-256-CBC-SHA"
 
-// Camellia ciphersuites from RFC 4132
+// Camellia ciphersuites from RFC4132
 #define TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA "CAMELLIA128-SHA"
 #define TLS1_TXT_DH_DSS_WITH_CAMELLIA_128_CBC_SHA "DH-DSS-CAMELLIA128-SHA"
 #define TLS1_TXT_DH_RSA_WITH_CAMELLIA_128_CBC_SHA "DH-RSA-CAMELLIA128-SHA"
@@ -554,7 +555,7 @@
 #define TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA "DHE-RSA-CAMELLIA256-SHA"
 #define TLS1_TXT_ADH_WITH_CAMELLIA_256_CBC_SHA "ADH-CAMELLIA256-SHA"
 
-// SEED ciphersuites from RFC 4162
+// SEED ciphersuites from RFC4162
 #define TLS1_TXT_RSA_WITH_SEED_SHA "SEED-SHA"
 #define TLS1_TXT_DH_DSS_WITH_SEED_SHA "DH-DSS-SEED-SHA"
 #define TLS1_TXT_DH_RSA_WITH_SEED_SHA "DH-RSA-SEED-SHA"
@@ -577,7 +578,7 @@
 #define TLS1_TXT_ADH_WITH_AES_128_SHA256 "ADH-AES128-SHA256"
 #define TLS1_TXT_ADH_WITH_AES_256_SHA256 "ADH-AES256-SHA256"
 
-// TLS v1.2 GCM ciphersuites from RFC 5288
+// TLS v1.2 GCM ciphersuites from RFC5288
 #define TLS1_TXT_RSA_WITH_AES_128_GCM_SHA256 "AES128-GCM-SHA256"
 #define TLS1_TXT_RSA_WITH_AES_256_GCM_SHA384 "AES256-GCM-SHA384"
 #define TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 "DHE-RSA-AES128-GCM-SHA256"
@@ -591,7 +592,7 @@
 #define TLS1_TXT_ADH_WITH_AES_128_GCM_SHA256 "ADH-AES128-GCM-SHA256"
 #define TLS1_TXT_ADH_WITH_AES_256_GCM_SHA384 "ADH-AES256-GCM-SHA384"
 
-// ECDH HMAC based ciphersuites from RFC 5289
+// ECDH HMAC based ciphersuites from RFC5289
 
 #define TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256 "ECDHE-ECDSA-AES128-SHA256"
 #define TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384 "ECDHE-ECDSA-AES256-SHA384"
@@ -602,7 +603,7 @@
 #define TLS1_TXT_ECDH_RSA_WITH_AES_128_SHA256 "ECDH-RSA-AES128-SHA256"
 #define TLS1_TXT_ECDH_RSA_WITH_AES_256_SHA384 "ECDH-RSA-AES256-SHA384"
 
-// ECDH GCM based ciphersuites from RFC 5289
+// ECDH GCM based ciphersuites from RFC5289
 #define TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \
   "ECDHE-ECDSA-AES128-GCM-SHA256"
 #define TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 \
diff --git a/src/include/openssl/x509.h b/src/include/openssl/x509.h
index 30ad4d2..dafa677 100644
--- a/src/include/openssl/x509.h
+++ b/src/include/openssl/x509.h
@@ -110,19 +110,28 @@
 #define X509v3_KU_DECIPHER_ONLY 0x8000
 #define X509v3_KU_UNDEF 0xffff
 
-struct X509_algor_st {
-  ASN1_OBJECT *algorithm;
-  ASN1_TYPE *parameter;
-} /* X509_ALGOR */;
-
-DECLARE_ASN1_FUNCTIONS(X509_ALGOR)
-
 DEFINE_STACK_OF(X509_ALGOR)
 
 typedef STACK_OF(X509_ALGOR) X509_ALGORS;
 
+struct X509_name_entry_st {
+  ASN1_OBJECT *object;
+  ASN1_STRING *value;
+  int set;
+} /* X509_NAME_ENTRY */;
+
 DEFINE_STACK_OF(X509_NAME_ENTRY)
 
+// we always keep X509_NAMEs in 2 forms.
+struct X509_name_st {
+  STACK_OF(X509_NAME_ENTRY) *entries;
+  int modified;  // true if 'bytes' needs to be built
+  BUF_MEM *bytes;
+  // unsigned long hash; Keep the hash around for lookups
+  unsigned char *canon_enc;
+  int canon_enclen;
+} /* X509_NAME */;
+
 DEFINE_STACK_OF(X509_NAME)
 
 typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS;
@@ -131,6 +140,20 @@
 
 DEFINE_STACK_OF(X509_ATTRIBUTE)
 
+struct x509_cinf_st {
+  ASN1_INTEGER *version;  // [ 0 ] default of v1
+  ASN1_INTEGER *serialNumber;
+  X509_ALGOR *signature;
+  X509_NAME *issuer;
+  X509_VAL *validity;
+  X509_NAME *subject;
+  X509_PUBKEY *key;
+  ASN1_BIT_STRING *issuerUID;            // [ 1 ] optional in v2
+  ASN1_BIT_STRING *subjectUID;           // [ 2 ] optional in v2
+  STACK_OF(X509_EXTENSION) *extensions;  // [ 3 ] optional in v3
+  ASN1_ENCODING enc;
+} /* X509_CINF */;
+
 // This stuff is certificate "auxiliary info"
 // it contains details which are useful in certificate
 // stores and databases. When used this is tagged onto
@@ -139,6 +162,31 @@
 DECLARE_STACK_OF(DIST_POINT)
 DECLARE_STACK_OF(GENERAL_NAME)
 
+struct x509_st {
+  X509_CINF *cert_info;
+  X509_ALGOR *sig_alg;
+  ASN1_BIT_STRING *signature;
+  CRYPTO_refcount_t references;
+  CRYPTO_EX_DATA ex_data;
+  // These contain copies of various extension values
+  long ex_pathlen;
+  long ex_pcpathlen;
+  unsigned long ex_flags;
+  unsigned long ex_kusage;
+  unsigned long ex_xkusage;
+  unsigned long ex_nscert;
+  ASN1_OCTET_STRING *skid;
+  AUTHORITY_KEYID *akid;
+  X509_POLICY_CACHE *policy_cache;
+  STACK_OF(DIST_POINT) *crldp;
+  STACK_OF(GENERAL_NAME) *altname;
+  NAME_CONSTRAINTS *nc;
+  unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+  X509_CERT_AUX *aux;
+  CRYPTO_BUFFER *buf;
+  CRYPTO_MUTEX lock;
+} /* X509 */;
+
 DEFINE_STACK_OF(X509)
 
 // This is used for a table of trust checking functions
@@ -206,7 +254,7 @@
 #define XN_FLAG_SEP_MASK (0xf << 16)
 
 #define XN_FLAG_COMPAT 0  // Traditional SSLeay: use old X509_NAME_print
-#define XN_FLAG_SEP_COMMA_PLUS (1 << 16)  // RFC 2253 ,+
+#define XN_FLAG_SEP_COMMA_PLUS (1 << 16)  // RFC2253 ,+
 #define XN_FLAG_SEP_CPLUS_SPC (2 << 16)   // ,+ spaced: more readable
 #define XN_FLAG_SEP_SPLUS_SPC (3 << 16)   // ;+ spaced
 #define XN_FLAG_SEP_MULTILINE (4 << 16)   // One line per field
@@ -225,13 +273,13 @@
 #define XN_FLAG_SPC_EQ (1 << 23)  // Put spaces round '='
 
 // This determines if we dump fields we don't recognise:
-// RFC 2253 requires this.
+// RFC2253 requires this.
 
 #define XN_FLAG_DUMP_UNKNOWN_FIELDS (1 << 24)
 
 #define XN_FLAG_FN_ALIGN (1 << 25)  // Align field names to 20 characters
 
-// Complete set of RFC 2253 flags
+// Complete set of RFC2253 flags
 
 #define XN_FLAG_RFC2253                                             \
   (ASN1_STRFLGS_RFC2253 | XN_FLAG_SEP_COMMA_PLUS | XN_FLAG_DN_REV | \
@@ -408,7 +456,7 @@
 #define X509_extract_key(x) X509_get_pubkey(x)
 
 // X509_get_pathlen returns path length constraint from the basic constraints
-// extension in |x509|. (See RFC 5280, section 4.2.1.9.) It returns -1 if the
+// extension in |x509|. (See RFC5280, section 4.2.1.9.) It returns -1 if the
 // constraint is not present, or if some extension in |x509| was invalid.
 //
 // Note that decoding an |X509| object will not check for invalid extensions. To
@@ -676,6 +724,7 @@
 // copying parts of it as a normal |d2i_X509| call would do.
 OPENSSL_EXPORT X509 *X509_parse_from_buffer(CRYPTO_BUFFER *buf);
 
+#ifndef OPENSSL_NO_FP_API
 OPENSSL_EXPORT X509 *d2i_X509_fp(FILE *fp, X509 **x509);
 OPENSSL_EXPORT int i2d_X509_fp(FILE *fp, X509 *x509);
 OPENSSL_EXPORT X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl);
@@ -709,6 +758,7 @@
 OPENSSL_EXPORT EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a);
 OPENSSL_EXPORT int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey);
 OPENSSL_EXPORT EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a);
+#endif
 
 OPENSSL_EXPORT X509 *d2i_X509_bio(BIO *bp, X509 **x509);
 OPENSSL_EXPORT int i2d_X509_bio(BIO *bp, X509 *x509);
@@ -810,30 +860,12 @@
 OPENSSL_EXPORT int X509_NAME_get0_der(X509_NAME *nm, const unsigned char **pder,
                                       size_t *pderlen);
 
-// X509_cmp_time compares |s| against |*t|. On success, it returns a negative
-// number if |s| <= |*t| and a positive number if |s| > |*t|. On error, it
-// returns zero. If |t| is NULL, it uses the current time instead of |*t|.
-//
-// WARNING: Unlike most comparison functions, this function returns zero on
-// error, not equality.
 OPENSSL_EXPORT int X509_cmp_time(const ASN1_TIME *s, time_t *t);
-
-// X509_cmp_current_time behaves like |X509_cmp_time| but compares |s| against
-// the current time.
 OPENSSL_EXPORT int X509_cmp_current_time(const ASN1_TIME *s);
-
-// X509_time_adj calls |X509_time_adj_ex| with |offset_day| equal to zero.
-OPENSSL_EXPORT ASN1_TIME *X509_time_adj(ASN1_TIME *s, long offset_sec,
-                                        time_t *t);
-
-// X509_time_adj_ex behaves like |ASN1_TIME_adj|, but adds an offset to |*t|. If
-// |t| is NULL, it uses the current time instead of |*t|.
+OPENSSL_EXPORT ASN1_TIME *X509_time_adj(ASN1_TIME *s, long adj, time_t *t);
 OPENSSL_EXPORT ASN1_TIME *X509_time_adj_ex(ASN1_TIME *s, int offset_day,
                                            long offset_sec, time_t *t);
-
-// X509_gmtime_adj behaves like |X509_time_adj_ex| but adds |offset_sec| to the
-// current time.
-OPENSSL_EXPORT ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long offset_sec);
+OPENSSL_EXPORT ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj);
 
 OPENSSL_EXPORT const char *X509_get_default_cert_area(void);
 OPENSSL_EXPORT const char *X509_get_default_cert_dir(void);
@@ -850,15 +882,7 @@
 
 DECLARE_ASN1_FUNCTIONS(X509_PUBKEY)
 
-// X509_PUBKEY_set serializes |pkey| into a newly-allocated |X509_PUBKEY|
-// structure. On success, it frees |*x|, sets |*x| to the new object, and
-// returns one. Otherwise, it returns zero.
 OPENSSL_EXPORT int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey);
-
-// X509_PUBKEY_get decodes the public key in |key| and returns an |EVP_PKEY| on
-// success, or NULL on error. The caller must release the result with
-// |EVP_PKEY_free| when done. The |EVP_PKEY| is cached in |key|, so callers must
-// not mutate the result.
 OPENSSL_EXPORT EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key);
 
 DECLARE_ASN1_FUNCTIONS(X509_SIG)
@@ -880,10 +904,10 @@
 
 DECLARE_ASN1_FUNCTIONS(X509_NAME)
 
-// X509_NAME_set makes a copy of |name|. On success, it frees |*xn|, sets |*xn|
-// to the copy, and returns one. Otherwise, it returns zero.
 OPENSSL_EXPORT int X509_NAME_set(X509_NAME **xn, X509_NAME *name);
 
+DECLARE_ASN1_FUNCTIONS(X509_CINF)
+
 DECLARE_ASN1_FUNCTIONS(X509)
 DECLARE_ASN1_FUNCTIONS(X509_CERT_AUX)
 
@@ -1087,7 +1111,7 @@
 // a known NID.
 OPENSSL_EXPORT int X509_REQ_get_signature_nid(const X509_REQ *req);
 
-// i2d_re_X509_REQ_tbs serializes the CertificationRequestInfo (see RFC 2986)
+// i2d_re_X509_REQ_tbs serializes the CertificationRequestInfo (see RFC2986)
 // portion of |req|. If |outp| is NULL, nothing is written. Otherwise, if
 // |*outp| is not NULL, the result is written to |*outp|, which must have enough
 // space available, and |*outp| is advanced just past the output. If |outp| is
@@ -1114,7 +1138,7 @@
 
 // X509_REQ_extension_nid returns one if |nid| is a supported CSR attribute type
 // for carrying extensions and zero otherwise. The supported types are
-// |NID_ext_req| (pkcs-9-at-extensionRequest from RFC 2985) and |NID_ms_ext_req|
+// |NID_ext_req| (pkcs-9-at-extensionRequest from RFC2985) and |NID_ms_ext_req|
 // (a Microsoft szOID_CERT_EXTENSIONS variant).
 OPENSSL_EXPORT int X509_REQ_extension_nid(int nid);
 
@@ -1122,7 +1146,7 @@
 // returns a newly-allocated |STACK_OF(X509_EXTENSION)| containing the result.
 // It returns NULL on error, or if |req| did not request extensions.
 //
-// This function supports both pkcs-9-at-extensionRequest from RFC 2985 and the
+// This function supports both pkcs-9-at-extensionRequest from RFC2985 and the
 // Microsoft szOID_CERT_EXTENSIONS variant.
 OPENSSL_EXPORT STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req);
 
@@ -1342,6 +1366,7 @@
 
 OPENSSL_EXPORT int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b);
 OPENSSL_EXPORT int X509_CRL_match(const X509_CRL *a, const X509_CRL *b);
+#ifndef OPENSSL_NO_FP_API
 OPENSSL_EXPORT int X509_print_ex_fp(FILE *bp, X509 *x, unsigned long nmflag,
                                     unsigned long cflag);
 OPENSSL_EXPORT int X509_print_fp(FILE *bp, X509 *x);
@@ -1349,6 +1374,7 @@
 OPENSSL_EXPORT int X509_REQ_print_fp(FILE *bp, X509_REQ *req);
 OPENSSL_EXPORT int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm,
                                          int indent, unsigned long flags);
+#endif
 
 OPENSSL_EXPORT int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase);
 OPENSSL_EXPORT int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent,
@@ -1887,16 +1913,12 @@
 OPENSSL_EXPORT int X509_TRUST_get_trust(const X509_TRUST *xp);
 
 
-struct rsa_pss_params_st {
+typedef struct rsa_pss_params_st {
   X509_ALGOR *hashAlgorithm;
   X509_ALGOR *maskGenAlgorithm;
   ASN1_INTEGER *saltLength;
   ASN1_INTEGER *trailerField;
-  // OpenSSL caches the MGF hash on |RSA_PSS_PARAMS| in some cases. None of the
-  // cases apply to BoringSSL, so this is always NULL, but Node expects the
-  // field to be present.
-  X509_ALGOR *maskHash;
-} /* RSA_PSS_PARAMS */;
+} RSA_PSS_PARAMS;
 
 DECLARE_ASN1_FUNCTIONS(RSA_PSS_PARAMS)
 
@@ -1936,6 +1958,10 @@
 BORINGSSL_MAKE_DELETER(X509_STORE_CTX, X509_STORE_CTX_free)
 BORINGSSL_MAKE_DELETER(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free)
 
+using ScopedX509_STORE_CTX =
+    internal::StackAllocated<X509_STORE_CTX, void, X509_STORE_CTX_zero,
+                             X509_STORE_CTX_cleanup>;
+
 BSSL_NAMESPACE_END
 
 }  // extern C++
diff --git a/src/include/openssl/x509_vfy.h b/src/include/openssl/x509_vfy.h
index d8781af..9b99f4a 100644
--- a/src/include/openssl/x509_vfy.h
+++ b/src/include/openssl/x509_vfy.h
@@ -99,8 +99,39 @@
 #define X509_LU_CRL 2
 #define X509_LU_PKEY 3
 
+typedef struct x509_object_st {
+  // one of the above types
+  int type;
+  union {
+    char *ptr;
+    X509 *x509;
+    X509_CRL *crl;
+    EVP_PKEY *pkey;
+  } data;
+} X509_OBJECT;
+
 DEFINE_STACK_OF(X509_LOOKUP)
 DEFINE_STACK_OF(X509_OBJECT)
+
+// This is a static that defines the function interface
+typedef struct x509_lookup_method_st {
+  const char *name;
+  int (*new_item)(X509_LOOKUP *ctx);
+  void (*free)(X509_LOOKUP *ctx);
+  int (*init)(X509_LOOKUP *ctx);
+  int (*shutdown)(X509_LOOKUP *ctx);
+  int (*ctrl)(X509_LOOKUP *ctx, int cmd, const char *argc, long argl,
+              char **ret);
+  int (*get_by_subject)(X509_LOOKUP *ctx, int type, X509_NAME *name,
+                        X509_OBJECT *ret);
+  int (*get_by_issuer_serial)(X509_LOOKUP *ctx, int type, X509_NAME *name,
+                              ASN1_INTEGER *serial, X509_OBJECT *ret);
+  int (*get_by_fingerprint)(X509_LOOKUP *ctx, int type, unsigned char *bytes,
+                            int len, X509_OBJECT *ret);
+  int (*get_by_alias)(X509_LOOKUP *ctx, int type, char *str, int len,
+                      X509_OBJECT *ret);
+} X509_LOOKUP_METHOD;
+
 DEFINE_STACK_OF(X509_VERIFY_PARAM)
 
 typedef int (*X509_STORE_CTX_verify_cb)(int, X509_STORE_CTX *);
@@ -122,8 +153,103 @@
     X509_STORE_CTX *ctx, X509_NAME *nm);
 typedef int (*X509_STORE_CTX_cleanup_fn)(X509_STORE_CTX *ctx);
 
+// This is used to hold everything.  It is used for all certificate
+// validation.  Once we have a certificate chain, the 'verify'
+// function is then called to actually check the cert chain.
+struct x509_store_st {
+  // The following is a cache of trusted certs
+  int cache;                    // if true, stash any hits
+  STACK_OF(X509_OBJECT) *objs;  // Cache of all objects
+  CRYPTO_MUTEX objs_lock;
+  STACK_OF(X509) *additional_untrusted;
+
+  // These are external lookup methods
+  STACK_OF(X509_LOOKUP) *get_cert_methods;
+
+  X509_VERIFY_PARAM *param;
+
+  // Callbacks for various operations
+  X509_STORE_CTX_verify_fn verify;          // called to verify a certificate
+  X509_STORE_CTX_verify_cb verify_cb;       // error callback
+  X509_STORE_CTX_get_issuer_fn get_issuer;  // get issuers cert from ctx
+  X509_STORE_CTX_check_issued_fn check_issued;  // check issued
+  X509_STORE_CTX_check_revocation_fn
+      check_revocation;                   // Check revocation status of chain
+  X509_STORE_CTX_get_crl_fn get_crl;      // retrieve CRL
+  X509_STORE_CTX_check_crl_fn check_crl;  // Check CRL validity
+  X509_STORE_CTX_cert_crl_fn cert_crl;    // Check certificate against CRL
+  X509_STORE_CTX_lookup_certs_fn lookup_certs;
+  X509_STORE_CTX_lookup_crls_fn lookup_crls;
+  X509_STORE_CTX_cleanup_fn cleanup;
+
+  CRYPTO_refcount_t references;
+} /* X509_STORE */;
+
 OPENSSL_EXPORT int X509_STORE_set_depth(X509_STORE *store, int depth);
 
+// This is the functions plus an instance of the local variables.
+struct x509_lookup_st {
+  int init;                    // have we been started
+  int skip;                    // don't use us.
+  X509_LOOKUP_METHOD *method;  // the functions
+  char *method_data;           // method data
+
+  X509_STORE *store_ctx;  // who owns us
+} /* X509_LOOKUP */;
+
+// This is a used when verifying cert chains.  Since the
+// gathering of the cert chain can take some time (and have to be
+// 'retried', this needs to be kept and passed around.
+struct x509_store_ctx_st  // X509_STORE_CTX
+{
+  X509_STORE *ctx;
+
+  // The following are set by the caller
+  X509 *cert;                 // The cert to check
+  STACK_OF(X509) *untrusted;  // chain of X509s - untrusted - passed in
+  STACK_OF(X509_CRL) *crls;   // set of CRLs passed in
+
+  X509_VERIFY_PARAM *param;
+  void *other_ctx;  // Other info for use with get_issuer()
+
+  // Callbacks for various operations
+  X509_STORE_CTX_verify_fn verify;          // called to verify a certificate
+  X509_STORE_CTX_verify_cb verify_cb;       // error callback
+  X509_STORE_CTX_get_issuer_fn get_issuer;  // get issuers cert from ctx
+  X509_STORE_CTX_check_issued_fn check_issued;  // check issued
+  X509_STORE_CTX_check_revocation_fn
+      check_revocation;                   // Check revocation status of chain
+  X509_STORE_CTX_get_crl_fn get_crl;      // retrieve CRL
+  X509_STORE_CTX_check_crl_fn check_crl;  // Check CRL validity
+  X509_STORE_CTX_cert_crl_fn cert_crl;    // Check certificate against CRL
+  X509_STORE_CTX_check_policy_fn check_policy;
+  X509_STORE_CTX_lookup_certs_fn lookup_certs;
+  X509_STORE_CTX_lookup_crls_fn lookup_crls;
+  X509_STORE_CTX_cleanup_fn cleanup;
+
+  // The following is built up
+  int valid;               // if 0, rebuild chain
+  int last_untrusted;      // index of last untrusted cert
+  STACK_OF(X509) *chain;   // chain of X509s - built up and trusted
+  X509_POLICY_TREE *tree;  // Valid policy tree
+
+  int explicit_policy;  // Require explicit policy value
+
+  // When something goes wrong, this is why
+  int error_depth;
+  int error;
+  X509 *current_cert;
+  X509 *current_issuer;   // cert currently being tested as valid issuer
+  X509_CRL *current_crl;  // current CRL
+
+  int current_crl_score;         // score of current CRL
+  unsigned int current_reasons;  // Reason mask
+
+  X509_STORE_CTX *parent;  // For CRL path validation: parent context
+
+  CRYPTO_EX_DATA ex_data;
+} /* X509_STORE_CTX */;
+
 OPENSSL_EXPORT void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth);
 
 #define X509_STORE_CTX_set_app_data(ctx, data) \
diff --git a/src/include/openssl/x509v3.h b/src/include/openssl/x509v3.h
index 8e4a511..7e65ab3 100644
--- a/src/include/openssl/x509v3.h
+++ b/src/include/openssl/x509v3.h
@@ -154,6 +154,8 @@
 #define X509V3_EXT_CTX_DEP 0x2
 #define X509V3_EXT_MULTILINE 0x4
 
+typedef BIT_STRING_BITNAME ENUMERATED_NAMES;
+
 struct BASIC_CONSTRAINTS_st {
   int ca;
   ASN1_INTEGER *pathlen;
@@ -483,30 +485,12 @@
     X509V3_EXT_METHOD *method, ASN1_BIT_STRING *bits,
     STACK_OF(CONF_VALUE) *extlist);
 
-// i2v_GENERAL_NAME serializes |gen| as a |CONF_VALUE|. If |ret| is non-NULL, it
-// appends the value to |ret| and returns |ret| on success or NULL on error. If
-// it returns NULL, the caller is still responsible for freeing |ret|. If |ret|
-// is NULL, it returns a newly-allocated |STACK_OF(CONF_VALUE)| containing the
-// result. |method| is ignored.
-//
-// Do not use this function. This is an internal implementation detail of the
-// human-readable print functions. If extracting a SAN list from a certificate,
-// look at |gen| directly.
 OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(
     X509V3_EXT_METHOD *method, GENERAL_NAME *gen, STACK_OF(CONF_VALUE) *ret);
 OPENSSL_EXPORT int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen);
 
 DECLARE_ASN1_FUNCTIONS(GENERAL_NAMES)
 
-// i2v_GENERAL_NAMES serializes |gen| as a list of |CONF_VALUE|s. If |ret| is
-// non-NULL, it appends the values to |ret| and returns |ret| on success or NULL
-// on error. If it returns NULL, the caller is still responsible for freeing
-// |ret|. If |ret| is NULL, it returns a newly-allocated |STACK_OF(CONF_VALUE)|
-// containing the results. |method| is ignored.
-//
-// Do not use this function. This is an internal implementation detail of the
-// human-readable print functions. If extracting a SAN list from a certificate,
-// look at |gen| directly.
 OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(
     X509V3_EXT_METHOD *method, GENERAL_NAMES *gen,
     STACK_OF(CONF_VALUE) *extlist);
@@ -620,35 +604,15 @@
 OPENSSL_EXPORT void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
                                    X509_REQ *req, X509_CRL *crl, int flags);
 
-// X509V3_add_value appends a |CONF_VALUE| containing |name| and |value| to
-// |*extlist|. It returns one on success and zero on error. If |*extlist| is
-// NULL, it sets |*extlist| to a newly-allocated |STACK_OF(CONF_VALUE)|
-// containing the result. Either |name| or |value| may be NULL to omit the
-// field.
-//
-// On failure, if |*extlist| was NULL, |*extlist| will remain NULL when the
-// function returns.
 OPENSSL_EXPORT int X509V3_add_value(const char *name, const char *value,
                                     STACK_OF(CONF_VALUE) **extlist);
-
-// X509V3_add_value_uchar behaves like |X509V3_add_value| but takes an
-// |unsigned char| pointer.
 OPENSSL_EXPORT int X509V3_add_value_uchar(const char *name,
                                           const unsigned char *value,
                                           STACK_OF(CONF_VALUE) **extlist);
-
-// X509V3_add_value_bool behaves like |X509V3_add_value| but stores the value
-// "TRUE" if |asn1_bool| is non-zero and "FALSE" otherwise.
 OPENSSL_EXPORT int X509V3_add_value_bool(const char *name, int asn1_bool,
                                          STACK_OF(CONF_VALUE) **extlist);
-
-// X509V3_add_value_bool behaves like |X509V3_add_value| but stores a string
-// representation of |aint|. Note this string representation may be decimal or
-// hexadecimal, depending on the size of |aint|.
-OPENSSL_EXPORT int X509V3_add_value_int(const char *name,
-                                        const ASN1_INTEGER *aint,
+OPENSSL_EXPORT int X509V3_add_value_int(const char *name, ASN1_INTEGER *aint,
                                         STACK_OF(CONF_VALUE) **extlist);
-
 OPENSSL_EXPORT char *i2s_ASN1_INTEGER(X509V3_EXT_METHOD *meth,
                                       const ASN1_INTEGER *aint);
 OPENSSL_EXPORT ASN1_INTEGER *s2i_ASN1_INTEGER(X509V3_EXT_METHOD *meth,
@@ -695,7 +659,7 @@
 // extension, or -1 if not found. If |out_idx| is non-NULL, duplicate extensions
 // are not treated as an error. Callers, however, should not rely on this
 // behavior as it may be removed in the future. Duplicate extensions are
-// forbidden in RFC 5280.
+// forbidden in RFC5280.
 //
 // WARNING: This function is difficult to use correctly. Callers should pass a
 // non-NULL |out_critical| and check both the return value and |*out_critical|
@@ -825,7 +789,7 @@
 OPENSSL_EXPORT uint32_t X509_get_extended_key_usage(X509 *x);
 
 // X509_get0_subject_key_id returns |x509|'s subject key identifier, if present.
-// (See RFC 5280, section 4.2.1.2.) It returns NULL if the extension is not
+// (See RFC5280, section 4.2.1.2.) It returns NULL if the extension is not
 // present or if some extension in |x509| was invalid.
 //
 // Note that decoding an |X509| object will not check for invalid extensions. To
@@ -834,7 +798,7 @@
 OPENSSL_EXPORT const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x509);
 
 // X509_get0_authority_key_id returns keyIdentifier of |x509|'s authority key
-// identifier, if the extension and field are present. (See RFC 5280,
+// identifier, if the extension and field are present. (See RFC5280,
 // section 4.2.1.1.) It returns NULL if the extension is not present, if it is
 // present but lacks a keyIdentifier field, or if some extension in |x509| was
 // invalid.
@@ -846,7 +810,7 @@
 
 // X509_get0_authority_issuer returns the authorityCertIssuer of |x509|'s
 // authority key identifier, if the extension and field are present. (See
-// RFC 5280, section 4.2.1.1.) It returns NULL if the extension is not present,
+// RFC5280, section 4.2.1.1.) It returns NULL if the extension is not present,
 // if it is present but lacks a authorityCertIssuer field, or if some extension
 // in |x509| was invalid.
 //
@@ -857,7 +821,7 @@
 
 // X509_get0_authority_serial returns the authorityCertSerialNumber of |x509|'s
 // authority key identifier, if the extension and field are present. (See
-// RFC 5280, section 4.2.1.1.) It returns NULL if the extension is not present,
+// RFC5280, section 4.2.1.1.) It returns NULL if the extension is not present,
 // if it is present but lacks a authorityCertSerialNumber field, or if some
 // extension in |x509| was invalid.
 //
@@ -938,11 +902,8 @@
 BORINGSSL_MAKE_DELETER(ACCESS_DESCRIPTION, ACCESS_DESCRIPTION_free)
 BORINGSSL_MAKE_DELETER(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
 BORINGSSL_MAKE_DELETER(BASIC_CONSTRAINTS, BASIC_CONSTRAINTS_free)
-// TODO(davidben): Move this to conf.h and rename to CONF_VALUE_free.
-BORINGSSL_MAKE_DELETER(CONF_VALUE, X509V3_conf_free)
 BORINGSSL_MAKE_DELETER(DIST_POINT, DIST_POINT_free)
 BORINGSSL_MAKE_DELETER(GENERAL_NAME, GENERAL_NAME_free)
-BORINGSSL_MAKE_DELETER(GENERAL_SUBTREE, GENERAL_SUBTREE_free)
 BORINGSSL_MAKE_DELETER(NAME_CONSTRAINTS, NAME_CONSTRAINTS_free)
 BORINGSSL_MAKE_DELETER(POLICY_MAPPING, POLICY_MAPPING_free)
 BORINGSSL_MAKE_DELETER(POLICYINFO, POLICYINFO_free)
@@ -1015,6 +976,5 @@
 #define X509V3_R_UNSUPPORTED_OPTION 160
 #define X509V3_R_UNSUPPORTED_TYPE 161
 #define X509V3_R_USER_TOO_LONG 162
-#define X509V3_R_INVALID_VALUE 163
 
 #endif
diff --git a/src/ssl/encrypted_client_hello.cc b/src/ssl/encrypted_client_hello.cc
index 64fee3d..b70f66c 100644
--- a/src/ssl/encrypted_client_hello.cc
+++ b/src/ssl/encrypted_client_hello.cc
@@ -31,6 +31,12 @@
 #include "internal.h"
 
 
+#if defined(OPENSSL_MSAN)
+#define NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory")))
+#else
+#define NO_SANITIZE_MEMORY
+#endif
+
 BSSL_NAMESPACE_BEGIN
 
 // ECH reuses the extension code point for the version number.
@@ -78,17 +84,159 @@
   return true;
 }
 
-static bool is_valid_client_hello_inner(SSL *ssl, uint8_t *out_alert,
-                                        Span<const uint8_t> body) {
-  // See draft-ietf-tls-esni-13, section 7.1.
-  SSL_CLIENT_HELLO client_hello;
+bool ssl_decode_client_hello_inner(
+    SSL *ssl, uint8_t *out_alert, Array<uint8_t> *out_client_hello_inner,
+    Span<const uint8_t> encoded_client_hello_inner,
+    const SSL_CLIENT_HELLO *client_hello_outer) {
+  SSL_CLIENT_HELLO client_hello_inner;
+  if (!ssl_client_hello_init(ssl, &client_hello_inner,
+                             encoded_client_hello_inner)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return false;
+  }
+  // TLS 1.3 ClientHellos must have extensions, and EncodedClientHelloInners use
+  // ClientHelloOuter's session_id.
+  if (client_hello_inner.extensions_len == 0 ||
+      client_hello_inner.session_id_len != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return false;
+  }
+  client_hello_inner.session_id = client_hello_outer->session_id;
+  client_hello_inner.session_id_len = client_hello_outer->session_id_len;
+
+  // Begin serializing a message containing the ClientHelloInner in |cbb|.
+  ScopedCBB cbb;
+  CBB body, extensions;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO) ||
+      !ssl_client_hello_write_without_extensions(&client_hello_inner, &body) ||
+      !CBB_add_u16_length_prefixed(&body, &extensions)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  // Sort the extensions in ClientHelloOuter, so ech_outer_extensions may be
+  // processed in O(n*log(n)) time, rather than O(n^2).
+  struct Extension {
+    uint16_t extension = 0;
+    Span<const uint8_t> body;
+    bool copied = false;
+  };
+
+  // MSan's libc interceptors do not handle |bsearch|. See b/182583130.
+  auto compare_extension = [](const void *a, const void *b)
+                               NO_SANITIZE_MEMORY -> int {
+    const Extension *extension_a = reinterpret_cast<const Extension *>(a);
+    const Extension *extension_b = reinterpret_cast<const Extension *>(b);
+    if (extension_a->extension < extension_b->extension) {
+      return -1;
+    } else if (extension_a->extension > extension_b->extension) {
+      return 1;
+    }
+    return 0;
+  };
+  GrowableArray<Extension> sorted_extensions;
+  CBS unsorted_extensions(MakeConstSpan(client_hello_outer->extensions,
+                                        client_hello_outer->extensions_len));
+  while (CBS_len(&unsorted_extensions) > 0) {
+    Extension extension;
+    CBS extension_body;
+    if (!CBS_get_u16(&unsorted_extensions, &extension.extension) ||
+        !CBS_get_u16_length_prefixed(&unsorted_extensions, &extension_body)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return false;
+    }
+    extension.body = extension_body;
+    if (!sorted_extensions.Push(extension)) {
+      return false;
+    }
+  }
+  qsort(sorted_extensions.data(), sorted_extensions.size(), sizeof(Extension),
+        compare_extension);
+
+  // Copy extensions from |client_hello_inner|, expanding ech_outer_extensions.
+  CBS inner_extensions(MakeConstSpan(client_hello_inner.extensions,
+                                     client_hello_inner.extensions_len));
+  while (CBS_len(&inner_extensions) > 0) {
+    uint16_t extension_id;
+    CBS extension_body;
+    if (!CBS_get_u16(&inner_extensions, &extension_id) ||
+        !CBS_get_u16_length_prefixed(&inner_extensions, &extension_body)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      return false;
+    }
+    if (extension_id != TLSEXT_TYPE_ech_outer_extensions) {
+      if (!CBB_add_u16(&extensions, extension_id) ||
+          !CBB_add_u16(&extensions, CBS_len(&extension_body)) ||
+          !CBB_add_bytes(&extensions, CBS_data(&extension_body),
+                         CBS_len(&extension_body))) {
+        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+        return false;
+      }
+      continue;
+    }
+
+    // Replace ech_outer_extensions with the corresponding outer extensions.
+    CBS outer_extensions;
+    if (!CBS_get_u8_length_prefixed(&extension_body, &outer_extensions) ||
+        CBS_len(&extension_body) != 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      return false;
+    }
+    while (CBS_len(&outer_extensions) > 0) {
+      uint16_t extension_needed;
+      if (!CBS_get_u16(&outer_extensions, &extension_needed)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+        return false;
+      }
+      if (extension_needed == TLSEXT_TYPE_encrypted_client_hello) {
+        *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+        return false;
+      }
+      // Find the referenced extension.
+      Extension key;
+      key.extension = extension_needed;
+      Extension *result = reinterpret_cast<Extension *>(
+          bsearch(&key, sorted_extensions.data(), sorted_extensions.size(),
+                  sizeof(Extension), compare_extension));
+      if (result == nullptr) {
+        *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+        return false;
+      }
+
+      // Extensions may be referenced at most once, to bound the result size.
+      if (result->copied) {
+        *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+        return false;
+      }
+      result->copied = true;
+
+      if (!CBB_add_u16(&extensions, extension_needed) ||
+          !CBB_add_u16(&extensions, result->body.size()) ||
+          !CBB_add_bytes(&extensions, result->body.data(),
+                         result->body.size())) {
+        OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+        return false;
+      }
+    }
+  }
+  if (!CBB_flush(&body)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  // See https://github.com/tlswg/draft-ietf-tls-esni/pull/411
   CBS extension;
-  if (!ssl_client_hello_init(ssl, &client_hello, body) ||
-      !ssl_client_hello_get_extension(&client_hello, &extension,
-                                      TLSEXT_TYPE_encrypted_client_hello) ||
-      CBS_len(&extension) != 1 ||  //
-      CBS_data(&extension)[0] != ECH_CLIENT_INNER ||
-      !ssl_client_hello_get_extension(&client_hello, &extension,
+  if (!ssl_client_hello_init(ssl, &client_hello_inner,
+                             MakeConstSpan(CBB_data(&body), CBB_len(&body))) ||
+      !ssl_client_hello_get_extension(&client_hello_inner, &extension,
+                                      TLSEXT_TYPE_ech_is_inner) ||
+      CBS_len(&extension) != 0 ||
+      ssl_client_hello_get_extension(&client_hello_inner, &extension,
+                                     TLSEXT_TYPE_encrypted_client_hello) ||
+      !ssl_client_hello_get_extension(&client_hello_inner, &extension,
                                       TLSEXT_TYPE_supported_versions)) {
     *out_alert = SSL_AD_ILLEGAL_PARAMETER;
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_CLIENT_HELLO_INNER);
@@ -119,131 +267,6 @@
       return false;
     }
   }
-  return true;
-}
-
-bool ssl_decode_client_hello_inner(
-    SSL *ssl, uint8_t *out_alert, Array<uint8_t> *out_client_hello_inner,
-    Span<const uint8_t> encoded_client_hello_inner,
-    const SSL_CLIENT_HELLO *client_hello_outer) {
-  SSL_CLIENT_HELLO client_hello_inner;
-  CBS cbs = encoded_client_hello_inner;
-  if (!ssl_parse_client_hello_with_trailing_data(ssl, &cbs,
-                                                 &client_hello_inner)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    return false;
-  }
-  // The remaining data is padding.
-  uint8_t padding;
-  while (CBS_get_u8(&cbs, &padding)) {
-    if (padding != 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-      return false;
-    }
-  }
-
-  // TLS 1.3 ClientHellos must have extensions, and EncodedClientHelloInners use
-  // ClientHelloOuter's session_id.
-  if (client_hello_inner.extensions_len == 0 ||
-      client_hello_inner.session_id_len != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    return false;
-  }
-  client_hello_inner.session_id = client_hello_outer->session_id;
-  client_hello_inner.session_id_len = client_hello_outer->session_id_len;
-
-  // Begin serializing a message containing the ClientHelloInner in |cbb|.
-  ScopedCBB cbb;
-  CBB body, extensions_cbb;
-  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO) ||
-      !ssl_client_hello_write_without_extensions(&client_hello_inner, &body) ||
-      !CBB_add_u16_length_prefixed(&body, &extensions_cbb)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return false;
-  }
-
-  auto inner_extensions = MakeConstSpan(client_hello_inner.extensions,
-                                        client_hello_inner.extensions_len);
-  CBS ext_list_wrapper;
-  if (!ssl_client_hello_get_extension(&client_hello_inner, &ext_list_wrapper,
-                                      TLSEXT_TYPE_ech_outer_extensions)) {
-    // No ech_outer_extensions. Copy everything.
-    if (!CBB_add_bytes(&extensions_cbb, inner_extensions.data(),
-                       inner_extensions.size())) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return false;
-    }
-  } else {
-    const size_t offset = CBS_data(&ext_list_wrapper) - inner_extensions.data();
-    auto inner_extensions_before =
-        inner_extensions.subspan(0, offset - 4 /* extension header */);
-    auto inner_extensions_after =
-        inner_extensions.subspan(offset + CBS_len(&ext_list_wrapper));
-    if (!CBB_add_bytes(&extensions_cbb, inner_extensions_before.data(),
-                       inner_extensions_before.size())) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return false;
-    }
-
-    // Expand ech_outer_extensions. See draft-ietf-tls-esni-13, Appendix B.
-    CBS ext_list;
-    if (!CBS_get_u8_length_prefixed(&ext_list_wrapper, &ext_list) ||
-        CBS_len(&ext_list) == 0 || CBS_len(&ext_list_wrapper) != 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      return false;
-    }
-    CBS outer_extensions;
-    CBS_init(&outer_extensions, client_hello_outer->extensions,
-             client_hello_outer->extensions_len);
-    while (CBS_len(&ext_list) != 0) {
-      // Find the next extension to copy.
-      uint16_t want;
-      if (!CBS_get_u16(&ext_list, &want)) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-        return false;
-      }
-      // Seek to |want| in |outer_extensions|. |ext_list| is required to match
-      // ClientHelloOuter in order.
-      uint16_t found;
-      CBS ext_body;
-      do {
-        if (CBS_len(&outer_extensions) == 0) {
-          *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-          OPENSSL_PUT_ERROR(SSL, SSL_R_OUTER_EXTENSION_NOT_FOUND);
-          return false;
-        }
-        if (!CBS_get_u16(&outer_extensions, &found) ||
-            !CBS_get_u16_length_prefixed(&outer_extensions, &ext_body)) {
-          OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-          return false;
-        }
-      } while (found != want);
-      // Copy the extension.
-      if (!CBB_add_u16(&extensions_cbb, found) ||
-          !CBB_add_u16(&extensions_cbb, CBS_len(&ext_body)) ||
-          !CBB_add_bytes(&extensions_cbb, CBS_data(&ext_body),
-                         CBS_len(&ext_body))) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-        return false;
-      }
-    }
-
-    if (!CBB_add_bytes(&extensions_cbb, inner_extensions_after.data(),
-                       inner_extensions_after.size())) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return false;
-    }
-  }
-  if (!CBB_flush(&body)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return false;
-  }
-
-  if (!is_valid_client_hello_inner(
-          ssl, out_alert, MakeConstSpan(CBB_data(&body), CBB_len(&body)))) {
-    return false;
-  }
 
   if (!ssl->method->finish_message(ssl, cbb.get(), out_client_hello_inner)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -252,31 +275,56 @@
   return true;
 }
 
-bool ssl_client_hello_decrypt(EVP_HPKE_CTX *hpke_ctx, Array<uint8_t> *out,
-                              bool *out_is_decrypt_error,
-                              const SSL_CLIENT_HELLO *client_hello_outer,
-                              Span<const uint8_t> payload) {
+bool ssl_client_hello_decrypt(
+    EVP_HPKE_CTX *hpke_ctx, Array<uint8_t> *out_encoded_client_hello_inner,
+    bool *out_is_decrypt_error, const SSL_CLIENT_HELLO *client_hello_outer,
+    uint16_t kdf_id, uint16_t aead_id, const uint8_t config_id,
+    Span<const uint8_t> enc, Span<const uint8_t> payload) {
   *out_is_decrypt_error = false;
 
-  // The ClientHelloOuterAAD is |client_hello_outer| with |payload| (which must
-  // point within |client_hello_outer->extensions|) replaced with zeros. See
-  // draft-ietf-tls-esni-13, section 5.2.
-  Array<uint8_t> aad;
-  if (!aad.CopyFrom(MakeConstSpan(client_hello_outer->client_hello,
-                                  client_hello_outer->client_hello_len))) {
+  // Compute the ClientHello portion of the ClientHelloOuterAAD value. See
+  // draft-ietf-tls-esni-10, section 5.2.
+  ScopedCBB aad;
+  CBB enc_cbb, outer_hello_cbb, extensions_cbb;
+  if (!CBB_init(aad.get(), 256) ||
+      !CBB_add_u16(aad.get(), kdf_id) ||
+      !CBB_add_u16(aad.get(), aead_id) ||
+      !CBB_add_u8(aad.get(), config_id) ||
+      !CBB_add_u16_length_prefixed(aad.get(), &enc_cbb) ||
+      !CBB_add_bytes(&enc_cbb, enc.data(), enc.size()) ||
+      !CBB_add_u24_length_prefixed(aad.get(), &outer_hello_cbb) ||
+      !ssl_client_hello_write_without_extensions(client_hello_outer,
+                                                 &outer_hello_cbb) ||
+      !CBB_add_u16_length_prefixed(&outer_hello_cbb, &extensions_cbb)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return false;
   }
 
-  // We assert with |uintptr_t| because the comparison would be UB if they
-  // didn't alias.
-  assert(reinterpret_cast<uintptr_t>(client_hello_outer->extensions) <=
-         reinterpret_cast<uintptr_t>(payload.data()));
-  assert(reinterpret_cast<uintptr_t>(client_hello_outer->extensions +
-                                     client_hello_outer->extensions_len) >=
-         reinterpret_cast<uintptr_t>(payload.data() + payload.size()));
-  Span<uint8_t> payload_aad = MakeSpan(aad).subspan(
-      payload.data() - client_hello_outer->client_hello, payload.size());
-  OPENSSL_memset(payload_aad.data(), 0, payload_aad.size());
+  CBS extensions(MakeConstSpan(client_hello_outer->extensions,
+                               client_hello_outer->extensions_len));
+  while (CBS_len(&extensions) > 0) {
+    uint16_t extension_id;
+    CBS extension_body;
+    if (!CBS_get_u16(&extensions, &extension_id) ||
+        !CBS_get_u16_length_prefixed(&extensions, &extension_body)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      return false;
+    }
+    if (extension_id == TLSEXT_TYPE_encrypted_client_hello) {
+      continue;
+    }
+    if (!CBB_add_u16(&extensions_cbb, extension_id) ||
+        !CBB_add_u16(&extensions_cbb, CBS_len(&extension_body)) ||
+        !CBB_add_bytes(&extensions_cbb, CBS_data(&extension_body),
+                       CBS_len(&extension_body))) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      return false;
+    }
+  }
+  if (!CBB_flush(aad.get())) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return false;
+  }
 
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   // In fuzzer mode, disable encryption to improve coverage. We reserve a short
@@ -288,75 +336,124 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
     return false;
   }
-  if (!out->CopyFrom(payload)) {
+  if (!out_encoded_client_hello_inner->CopyFrom(payload)) {
     return false;
   }
 #else
-  // Attempt to decrypt into |out|.
-  if (!out->Init(payload.size())) {
+  // Attempt to decrypt into |out_encoded_client_hello_inner|.
+  if (!out_encoded_client_hello_inner->Init(payload.size())) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     return false;
   }
-  size_t len;
-  if (!EVP_HPKE_CTX_open(hpke_ctx, out->data(), &len, out->size(),
-                         payload.data(), payload.size(), aad.data(),
-                         aad.size())) {
+  size_t encoded_client_hello_inner_len;
+  if (!EVP_HPKE_CTX_open(hpke_ctx, out_encoded_client_hello_inner->data(),
+                         &encoded_client_hello_inner_len,
+                         out_encoded_client_hello_inner->size(), payload.data(),
+                         payload.size(), CBB_data(aad.get()),
+                         CBB_len(aad.get()))) {
     *out_is_decrypt_error = true;
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
     return false;
   }
-  out->Shrink(len);
+  out_encoded_client_hello_inner->Shrink(encoded_client_hello_inner_len);
 #endif
   return true;
 }
 
-static bool is_hex_component(Span<const uint8_t> in) {
-  if (in.size() < 2 || in[0] != '0' || (in[1] != 'x' && in[1] != 'X')) {
-    return false;
+static bool parse_ipv4_number(Span<const uint8_t> in, uint32_t *out) {
+  // See https://url.spec.whatwg.org/#ipv4-number-parser.
+  uint32_t base = 10;
+  if (in.size() >= 2 && in[0] == '0' && (in[1] == 'x' || in[1] == 'X')) {
+    in = in.subspan(2);
+    base = 16;
+  } else if (in.size() >= 1 && in[0] == '0') {
+    in = in.subspan(1);
+    base = 8;
   }
-  for (uint8_t b : in.subspan(2)) {
-    if (!('0' <= b && b <= '9') && !('a' <= b && b <= 'f') &&
-        !('A' <= b && b <= 'F')) {
+  *out = 0;
+  for (uint8_t c : in) {
+    uint32_t d;
+    if ('0' <= c && c <= '9') {
+      d = c - '0';
+    } else if ('a' <= c && c <= 'f') {
+      d = c - 'a' + 10;
+    } else if ('A' <= c && c <= 'F') {
+      d = c - 'A' + 10;
+    } else {
       return false;
     }
+    if (d >= base ||
+        *out > UINT32_MAX / base) {
+      return false;
+    }
+    *out *= base;
+    if (*out > UINT32_MAX - d) {
+      return false;
+    }
+    *out += d;
   }
   return true;
 }
 
-static bool is_decimal_component(Span<const uint8_t> in) {
-  if (in.empty()) {
+static bool is_ipv4_address(Span<const uint8_t> in) {
+  // See https://url.spec.whatwg.org/#concept-ipv4-parser
+  uint32_t numbers[4];
+  size_t num_numbers = 0;
+  while (!in.empty()) {
+    if (num_numbers == 4) {
+      // Too many components.
+      return false;
+    }
+    // Find the next dot-separated component.
+    auto dot = std::find(in.begin(), in.end(), '.');
+    if (dot == in.begin()) {
+      // Empty components are not allowed.
+      return false;
+    }
+    Span<const uint8_t> component;
+    if (dot == in.end()) {
+      component = in;
+      in = Span<const uint8_t>();
+    } else {
+      component = in.subspan(0, dot - in.begin());
+      in = in.subspan(dot - in.begin() + 1);  // Skip the dot.
+    }
+    if (!parse_ipv4_number(component, &numbers[num_numbers])) {
+      return false;
+    }
+    num_numbers++;
+  }
+  if (num_numbers == 0) {
     return false;
   }
-  for (uint8_t b : in) {
-    if (!('0' <= b && b <= '9')) {
+  for (size_t i = 0; i < num_numbers - 1; i++) {
+    if (numbers[i] > 255) {
       return false;
     }
   }
-  return true;
+  return num_numbers == 1 ||
+         numbers[num_numbers - 1] < 1u << (8 * (5 - num_numbers));
 }
 
 bool ssl_is_valid_ech_public_name(Span<const uint8_t> public_name) {
-  // See draft-ietf-tls-esni-13, Section 4 and RFC 5890, Section 2.3.1. The
+  // See draft-ietf-tls-esni-11, Section 4 and RFC5890, Section 2.3.1. The
   // public name must be a dot-separated sequence of LDH labels and not begin or
   // end with a dot.
-  auto remaining = public_name;
-  if (remaining.empty()) {
+  auto copy = public_name;
+  if (copy.empty()) {
     return false;
   }
-  Span<const uint8_t> last;
-  while (!remaining.empty()) {
+  while (!copy.empty()) {
     // Find the next dot-separated component.
-    auto dot = std::find(remaining.begin(), remaining.end(), '.');
+    auto dot = std::find(copy.begin(), copy.end(), '.');
     Span<const uint8_t> component;
-    if (dot == remaining.end()) {
-      component = remaining;
-      last = component;
-      remaining = Span<const uint8_t>();
+    if (dot == copy.end()) {
+      component = copy;
+      copy = Span<const uint8_t>();
     } else {
-      component = remaining.subspan(0, dot - remaining.begin());
-      // Skip the dot.
-      remaining = remaining.subspan(dot - remaining.begin() + 1);
-      if (remaining.empty()) {
+      component = copy.subspan(0, dot - copy.begin());
+      copy = copy.subspan(dot - copy.begin() + 1);  // Skip the dot.
+      if (copy.empty()) {
         // Trailing dots are not allowed.
         return false;
       }
@@ -375,15 +472,7 @@
     }
   }
 
-  // The WHATWG URL parser additionally does not allow any DNS names that end in
-  // a numeric component. See:
-  // https://url.spec.whatwg.org/#concept-host-parser
-  // https://url.spec.whatwg.org/#ends-in-a-number-checker
-  //
-  // The WHATWG parser is formulated in terms of parsing decimal, octal, and
-  // hex, along with a separate ASCII digits check. The ASCII digits check
-  // subsumes the decimal and octal check, so we only need to check two cases.
-  return !is_hex_component(last) && !is_decimal_component(last);
+  return !is_ipv4_address(public_name);
 }
 
 static bool parse_ech_config(CBS *cbs, ECHConfig *out, bool *out_supported,
@@ -419,8 +508,8 @@
       CBS_len(&public_key) == 0 ||
       !CBS_get_u16_length_prefixed(&contents, &cipher_suites) ||
       CBS_len(&cipher_suites) == 0 || CBS_len(&cipher_suites) % 4 != 0 ||
-      !CBS_get_u8(&contents, &out->maximum_name_length) ||
-      !CBS_get_u8_length_prefixed(&contents, &public_name) ||
+      !CBS_get_u16(&contents, &out->maximum_name_length) ||
+      !CBS_get_u16_length_prefixed(&contents, &public_name) ||
       CBS_len(&public_name) == 0 ||
       !CBS_get_u16_length_prefixed(&contents, &extensions) ||
       CBS_len(&contents) != 0) {
@@ -684,6 +773,15 @@
 #endif
 }
 
+static size_t compute_extension_length(const EVP_HPKE_AEAD *aead,
+                                       size_t enc_len, size_t in_len) {
+  size_t ret = 4;      // HpkeSymmetricCipherSuite cipher_suite
+  ret++;               // uint8 config_id
+  ret += 2 + enc_len;  // opaque enc<1..2^16-1>
+  ret += 2 + in_len + aead_overhead(aead);  // opaque payload<1..2^16-1>
+  return ret;
+}
+
 // random_size returns a random value between |min| and |max|, inclusive.
 static size_t random_size(size_t min, size_t max) {
   assert(min < max);
@@ -716,32 +814,38 @@
   //   2+32+1+2   version, random, legacy_session_id, legacy_compression_methods
   //   2+4*2      cipher_suites (three TLS 1.3 ciphers, GREASE)
   //   2          extensions prefix
-  //   5          inner encrypted_client_hello
+  //   4          ech_is_inner
   //   4+1+2*2    supported_versions (TLS 1.3, GREASE)
   //   4+1+10*2   outer_extensions (key_share, sigalgs, sct, alpn,
   //              supported_groups, status_request, psk_key_exchange_modes,
   //              compress_certificate, GREASE x2)
   //
   // The server_name extension has an overhead of 9 bytes. For now, arbitrarily
-  // estimate maximum_name_length to be between 32 and 100 bytes. Then round up
-  // to a multiple of 32, to match draft-ietf-tls-esni-13, section 6.1.3.
-  const size_t payload_len =
-      32 * random_size(128 / 32, 224 / 32) + aead_overhead(aead);
+  // estimate maximum_name_length to be between 32 and 100 bytes.
+  //
+  // TODO(https://crbug.com/boringssl/275): If the padding scheme changes to
+  // also round the entire payload, adjust this to match. See
+  // https://github.com/tlswg/draft-ietf-tls-esni/issues/433
+  const size_t overhead = aead_overhead(aead);
+  const size_t in_len = random_size(128, 196);
+  const size_t extension_len =
+      compute_extension_length(aead, sizeof(enc), in_len);
   bssl::ScopedCBB cbb;
   CBB enc_cbb, payload_cbb;
   uint8_t *payload;
-  if (!CBB_init(cbb.get(), 256) ||
+  if (!CBB_init(cbb.get(), extension_len) ||
       !CBB_add_u16(cbb.get(), kdf_id) ||
       !CBB_add_u16(cbb.get(), EVP_HPKE_AEAD_id(aead)) ||
       !CBB_add_u8(cbb.get(), config_id) ||
       !CBB_add_u16_length_prefixed(cbb.get(), &enc_cbb) ||
       !CBB_add_bytes(&enc_cbb, enc, sizeof(enc)) ||
       !CBB_add_u16_length_prefixed(cbb.get(), &payload_cbb) ||
-      !CBB_add_space(&payload_cbb, &payload, payload_len) ||
-      !RAND_bytes(payload, payload_len) ||
-      !CBBFinishArray(cbb.get(), &hs->ech_client_outer)) {
+      !CBB_add_space(&payload_cbb, &payload, in_len + overhead) ||
+      !RAND_bytes(payload, in_len + overhead) ||
+      !CBBFinishArray(cbb.get(), &hs->ech_client_bytes)) {
     return false;
   }
+  assert(hs->ech_client_bytes.size() == extension_len);
   return true;
 }
 
@@ -752,22 +856,22 @@
   }
 
   // Construct ClientHelloInner and EncodedClientHelloInner. See
-  // draft-ietf-tls-esni-13, sections 5.1 and 6.1.
-  ScopedCBB cbb, encoded_cbb;
+  // draft-ietf-tls-esni-10, sections 5.1 and 6.1.
+  bssl::ScopedCBB cbb, encoded;
   CBB body;
   bool needs_psk_binder;
-  Array<uint8_t> hello_inner;
+  bssl::Array<uint8_t> hello_inner;
   if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO) ||
-      !CBB_init(encoded_cbb.get(), 256) ||
+      !CBB_init(encoded.get(), 256) ||
       !ssl_write_client_hello_without_extensions(hs, &body,
                                                  ssl_client_hello_inner,
                                                  /*empty_session_id=*/false) ||
-      !ssl_write_client_hello_without_extensions(hs, encoded_cbb.get(),
+      !ssl_write_client_hello_without_extensions(hs, encoded.get(),
                                                  ssl_client_hello_inner,
                                                  /*empty_session_id=*/true) ||
-      !ssl_add_clienthello_tlsext(hs, &body, encoded_cbb.get(),
-                                  &needs_psk_binder, ssl_client_hello_inner,
-                                  CBB_len(&body)) ||
+      !ssl_add_clienthello_tlsext(hs, &body, encoded.get(), &needs_psk_binder,
+                                  ssl_client_hello_inner, CBB_len(&body),
+                                  /*omit_ech_len=*/0) ||
       !ssl->method->finish_message(ssl, cbb.get(), &hello_inner)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
@@ -780,12 +884,13 @@
       return false;
     }
     // Also update the EncodedClientHelloInner.
-    auto encoded_binder =
-        MakeSpan(const_cast<uint8_t *>(CBB_data(encoded_cbb.get())),
-                 CBB_len(encoded_cbb.get()))
-            .last(binder_len);
-    auto hello_inner_binder = MakeConstSpan(hello_inner).last(binder_len);
-    OPENSSL_memcpy(encoded_binder.data(), hello_inner_binder.data(),
+    if (CBB_len(encoded.get()) < binder_len) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return false;
+    }
+    OPENSSL_memcpy(const_cast<uint8_t *>(CBB_data(encoded.get())) +
+                       CBB_len(encoded.get()) - binder_len,
+                   hello_inner.data() + hello_inner.size() - binder_len,
                    binder_len);
   }
 
@@ -793,82 +898,74 @@
     return false;
   }
 
-  // Pad the EncodedClientHelloInner. See draft-ietf-tls-esni-13, section 6.1.3.
-  size_t padding_len = 0;
-  size_t maximum_name_length = hs->selected_ech_config->maximum_name_length;
-  if (ssl->hostname) {
-    size_t hostname_len = strlen(ssl->hostname.get());
-    if (hostname_len <= maximum_name_length) {
-      padding_len = maximum_name_length - hostname_len;
-    }
-  } else {
-    // No SNI. Pad up to |maximum_name_length|, including server_name extension
-    // overhead.
-    padding_len = 9 + maximum_name_length;
-  }
-  // Pad the whole thing to a multiple of 32 bytes.
-  padding_len += 31 - ((CBB_len(encoded_cbb.get()) + padding_len - 1) % 32);
-  Array<uint8_t> encoded;
-  if (!CBB_add_zeros(encoded_cbb.get(), padding_len) ||
-      !CBBFinishArray(encoded_cbb.get(), &encoded)) {
-    return false;
-  }
-
-  // Encrypt |encoded|. See draft-ietf-tls-esni-13, section 6.1.1. First,
-  // assemble the extension with a placeholder value for ClientHelloOuterAAD.
-  // See draft-ietf-tls-esni-13, section 5.2.
+  // Construct ClientHelloOuterAAD. See draft-ietf-tls-esni-10, section 5.2.
+  // TODO(https://crbug.com/boringssl/275): This ends up constructing the
+  // ClientHelloOuter twice. Revisit this in the next draft, which uses a more
+  // forgiving construction.
   const EVP_HPKE_KDF *kdf = EVP_HPKE_CTX_kdf(hs->ech_hpke_ctx.get());
   const EVP_HPKE_AEAD *aead = EVP_HPKE_CTX_aead(hs->ech_hpke_ctx.get());
-  size_t payload_len = encoded.size() + aead_overhead(aead);
-  CBB enc_cbb, payload_cbb;
-  if (!CBB_init(cbb.get(), 256) ||
+  const size_t extension_len =
+      compute_extension_length(aead, enc.size(), CBB_len(encoded.get()));
+  bssl::ScopedCBB aad;
+  CBB outer_hello;
+  CBB enc_cbb;
+  if (!CBB_init(aad.get(), 256) ||
+      !CBB_add_u16(aad.get(), EVP_HPKE_KDF_id(kdf)) ||
+      !CBB_add_u16(aad.get(), EVP_HPKE_AEAD_id(aead)) ||
+      !CBB_add_u8(aad.get(), hs->selected_ech_config->config_id) ||
+      !CBB_add_u16_length_prefixed(aad.get(), &enc_cbb) ||
+      !CBB_add_bytes(&enc_cbb, enc.data(), enc.size()) ||
+      !CBB_add_u24_length_prefixed(aad.get(), &outer_hello) ||
+      !ssl_write_client_hello_without_extensions(hs, &outer_hello,
+                                                 ssl_client_hello_outer,
+                                                 /*empty_session_id=*/false) ||
+      !ssl_add_clienthello_tlsext(hs, &outer_hello, /*out_encoded=*/nullptr,
+                                  &needs_psk_binder, ssl_client_hello_outer,
+                                  CBB_len(&outer_hello),
+                                  /*omit_ech_len=*/4 + extension_len) ||
+      !CBB_flush(aad.get())) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  // ClientHelloOuter may not require a PSK binder. Otherwise, we have a
+  // circular dependency.
+  assert(!needs_psk_binder);
+
+  CBB payload_cbb;
+  if (!CBB_init(cbb.get(), extension_len) ||
       !CBB_add_u16(cbb.get(), EVP_HPKE_KDF_id(kdf)) ||
       !CBB_add_u16(cbb.get(), EVP_HPKE_AEAD_id(aead)) ||
       !CBB_add_u8(cbb.get(), hs->selected_ech_config->config_id) ||
       !CBB_add_u16_length_prefixed(cbb.get(), &enc_cbb) ||
       !CBB_add_bytes(&enc_cbb, enc.data(), enc.size()) ||
-      !CBB_add_u16_length_prefixed(cbb.get(), &payload_cbb) ||
-      !CBB_add_zeros(&payload_cbb, payload_len) ||
-      !CBBFinishArray(cbb.get(), &hs->ech_client_outer)) {
+      !CBB_add_u16_length_prefixed(cbb.get(), &payload_cbb)) {
     return false;
   }
-
-  // Construct ClientHelloOuterAAD.
-  // TODO(https://crbug.com/boringssl/275): This ends up constructing the
-  // ClientHelloOuter twice. Instead, reuse |aad| for the ClientHello, now that
-  // draft-12 made the length prefixes match.
-  bssl::ScopedCBB aad;
-  if (!CBB_init(aad.get(), 256) ||
-      !ssl_write_client_hello_without_extensions(hs, aad.get(),
-                                                 ssl_client_hello_outer,
-                                                 /*empty_session_id=*/false) ||
-      !ssl_add_clienthello_tlsext(hs, aad.get(), /*out_encoded=*/nullptr,
-                                  &needs_psk_binder, ssl_client_hello_outer,
-                                  CBB_len(aad.get()))) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return false;
-  }
-
-  // ClientHelloOuter may not require a PSK binder. Otherwise, we have a
-  // circular dependency.
-  assert(!needs_psk_binder);
-
-  // Replace the payload in |hs->ech_client_outer| with the encrypted value.
-  auto payload_span = MakeSpan(hs->ech_client_outer).last(payload_len);
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   // In fuzzer mode, the server expects a cleartext payload.
-  assert(payload_span.size() == encoded.size());
-  OPENSSL_memcpy(payload_span.data(), encoded.data(), encoded.size());
+  if (!CBB_add_bytes(&payload_cbb, CBB_data(encoded.get()),
+                     CBB_len(encoded.get()))) {
+    return false;
+  }
 #else
-  if (!EVP_HPKE_CTX_seal(hs->ech_hpke_ctx.get(), payload_span.data(),
-                         &payload_len, payload_span.size(), encoded.data(),
-                         encoded.size(), CBB_data(aad.get()),
+  uint8_t *payload;
+  size_t payload_len =
+      CBB_len(encoded.get()) + EVP_AEAD_max_overhead(EVP_HPKE_AEAD_aead(aead));
+  if (!CBB_reserve(&payload_cbb, &payload, payload_len) ||
+      !EVP_HPKE_CTX_seal(hs->ech_hpke_ctx.get(), payload, &payload_len,
+                         payload_len, CBB_data(encoded.get()),
+                         CBB_len(encoded.get()), CBB_data(aad.get()),
                          CBB_len(aad.get())) ||
-      payload_len != payload_span.size()) {
+      !CBB_did_write(&payload_cbb, payload_len)) {
     return false;
   }
 #endif // BORINGSSL_UNSAFE_FUZZER_MODE
+  if (!CBBFinishArray(cbb.get(), &hs->ech_client_bytes)) {
+    return false;
+  }
 
+  // The |aad| calculation relies on |extension_length| being correct.
+  assert(hs->ech_client_bytes.size() == extension_len);
   return true;
 }
 
@@ -948,13 +1045,7 @@
     return 0;
   }
 
-  // The maximum name length is encoded in one byte.
-  if (max_name_len > 0xff) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_LENGTH);
-    return 0;
-  }
-
-  // See draft-ietf-tls-esni-13, section 4.
+  // See draft-ietf-tls-esni-10, section 4.
   ScopedCBB cbb;
   CBB contents, child;
   uint8_t *public_key;
@@ -975,8 +1066,8 @@
       !CBB_add_u16(&child, EVP_HPKE_AES_128_GCM) ||
       !CBB_add_u16(&child, EVP_HPKE_HKDF_SHA256) ||
       !CBB_add_u16(&child, EVP_HPKE_CHACHA20_POLY1305) ||
-      !CBB_add_u8(&contents, max_name_len) ||
-      !CBB_add_u8_length_prefixed(&contents, &child) ||
+      !CBB_add_u16(&contents, max_name_len) ||
+      !CBB_add_u16_length_prefixed(&contents, &child) ||
       !CBB_add_bytes(&child, public_name_u8.data(), public_name_u8.size()) ||
       // TODO(https://crbug.com/boringssl/275): Reserve some GREASE extensions
       // and include some.
diff --git a/src/ssl/extensions.cc b/src/ssl/extensions.cc
index 3baef6d..4950d26 100644
--- a/src/ssl/extensions.cc
+++ b/src/ssl/extensions.cc
@@ -210,24 +210,16 @@
 
 bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out,
                            Span<const uint8_t> body) {
-  CBS cbs = body;
-  if (!ssl_parse_client_hello_with_trailing_data(ssl, &cbs, out) ||
-      CBS_len(&cbs) != 0) {
-    return false;
-  }
-  return true;
-}
-
-bool ssl_parse_client_hello_with_trailing_data(const SSL *ssl, CBS *cbs,
-                                               SSL_CLIENT_HELLO *out) {
   OPENSSL_memset(out, 0, sizeof(*out));
   out->ssl = const_cast<SSL *>(ssl);
+  out->client_hello = body.data();
+  out->client_hello_len = body.size();
 
-  CBS copy = *cbs;
-  CBS random, session_id;
-  if (!CBS_get_u16(cbs, &out->version) ||
-      !CBS_get_bytes(cbs, &random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(cbs, &session_id) ||
+  CBS client_hello, random, session_id;
+  CBS_init(&client_hello, out->client_hello, out->client_hello_len);
+  if (!CBS_get_u16(&client_hello, &out->version) ||
+      !CBS_get_bytes(&client_hello, &random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&client_hello, &session_id) ||
       CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) {
     return false;
   }
@@ -240,16 +232,16 @@
   // Skip past DTLS cookie
   if (SSL_is_dtls(out->ssl)) {
     CBS cookie;
-    if (!CBS_get_u8_length_prefixed(cbs, &cookie) ||
+    if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) ||
         CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) {
       return false;
     }
   }
 
   CBS cipher_suites, compression_methods;
-  if (!CBS_get_u16_length_prefixed(cbs, &cipher_suites) ||
+  if (!CBS_get_u16_length_prefixed(&client_hello, &cipher_suites) ||
       CBS_len(&cipher_suites) < 2 || (CBS_len(&cipher_suites) & 1) != 0 ||
-      !CBS_get_u8_length_prefixed(cbs, &compression_methods) ||
+      !CBS_get_u8_length_prefixed(&client_hello, &compression_methods) ||
       CBS_len(&compression_methods) < 1) {
     return false;
   }
@@ -261,22 +253,23 @@
 
   // If the ClientHello ends here then it's valid, but doesn't have any
   // extensions.
-  if (CBS_len(cbs) == 0) {
-    out->extensions = nullptr;
+  if (CBS_len(&client_hello) == 0) {
+    out->extensions = NULL;
     out->extensions_len = 0;
-  } else {
-    // Extract extensions and check it is valid.
-    CBS extensions;
-    if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
-        !tls1_check_duplicate_extensions(&extensions)) {
-      return false;
-    }
-    out->extensions = CBS_data(&extensions);
-    out->extensions_len = CBS_len(&extensions);
+    return true;
   }
 
-  out->client_hello = CBS_data(&copy);
-  out->client_hello_len = CBS_len(&copy) - CBS_len(cbs);
+  // Extract extensions and check it is valid.
+  CBS extensions;
+  if (!CBS_get_u16_length_prefixed(&client_hello, &extensions) ||
+      !tls1_check_duplicate_extensions(&extensions) ||
+      CBS_len(&client_hello) != 0) {
+    return false;
+  }
+
+  out->extensions = CBS_data(&extensions);
+  out->extensions_len = CBS_len(&extensions);
+
   return true;
 }
 
@@ -626,30 +619,20 @@
 
 // Encrypted ClientHello (ECH)
 //
-// https://tools.ietf.org/html/draft-ietf-tls-esni-13
+// https://tools.ietf.org/html/draft-ietf-tls-esni-10
 
 static bool ext_ech_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
                                     CBB *out_compressible,
                                     ssl_client_hello_type_t type) {
-  if (type == ssl_client_hello_inner) {
-    if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_client_hello) ||
-        !CBB_add_u16(out, /* length */ 1) ||
-        !CBB_add_u8(out, ECH_CLIENT_INNER)) {
-      return false;
-    }
-    return true;
-  }
-
-  if (hs->ech_client_outer.empty()) {
+  if (type == ssl_client_hello_inner || hs->ech_client_bytes.empty()) {
     return true;
   }
 
   CBB ech_body;
   if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_client_hello) ||
       !CBB_add_u16_length_prefixed(out, &ech_body) ||
-      !CBB_add_u8(&ech_body, ECH_CLIENT_OUTER) ||
-      !CBB_add_bytes(&ech_body, hs->ech_client_outer.data(),
-                     hs->ech_client_outer.size()) ||
+      !CBB_add_bytes(&ech_body, hs->ech_client_bytes.data(),
+                     hs->ech_client_bytes.size()) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -664,10 +647,8 @@
   }
 
   // The ECH extension may not be sent in TLS 1.2 ServerHello, only TLS 1.3
-  // EncryptedExtensions. It also may not be sent in response to an inner ECH
-  // extension.
-  if (ssl_protocol_version(ssl) < TLS1_3_VERSION ||
-      ssl->s3->ech_status == ssl_ech_accepted) {
+  // EncryptedExtension.
+  if (ssl_protocol_version(ssl) < TLS1_3_VERSION) {
     *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
     return false;
@@ -678,7 +659,17 @@
     return false;
   }
 
-  if (ssl->s3->ech_status == ssl_ech_rejected &&
+  // The server may only send retry configs in response to ClientHelloOuter (or
+  // ECH GREASE), not ClientHelloInner. The unsolicited extension rule checks
+  // this implicitly because the ClientHelloInner has no encrypted_client_hello
+  // extension.
+  //
+  // TODO(https://crbug.com/boringssl/275): If
+  // https://github.com/tlswg/draft-ietf-tls-esni/pull/422 is merged, a later
+  // draft will fold encrypted_client_hello and ech_is_inner together. Then this
+  // assert should become a runtime check.
+  assert(ssl->s3->ech_status != ssl_ech_accepted);
+  if (hs->selected_ech_config &&
       !hs->ech_retry_configs.CopyFrom(*contents)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return false;
@@ -689,23 +680,10 @@
 
 static bool ext_ech_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                       CBS *contents) {
-  if (contents == nullptr) {
+  if (contents != nullptr) {
+    hs->ech_present = true;
     return true;
   }
-
-  uint8_t type;
-  if (!CBS_get_u8(contents, &type)) {
-    return false;
-  }
-  if (type == ECH_CLIENT_OUTER) {
-    // Outer ECH extensions are handled outside the callback.
-    return true;
-  }
-  if (type != ECH_CLIENT_INNER || CBS_len(contents) != 0) {
-    return false;
-  }
-
-  hs->ech_is_inner = true;
   return true;
 }
 
@@ -737,6 +715,32 @@
   return CBB_flush(out);
 }
 
+static bool ext_ech_is_inner_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                             CBB *out_compressible,
+                                             ssl_client_hello_type_t type) {
+  if (type == ssl_client_hello_inner) {
+    if (!CBB_add_u16(out, TLSEXT_TYPE_ech_is_inner) ||
+        !CBB_add_u16(out, 0 /* empty extension */)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static bool ext_ech_is_inner_parse_clienthello(SSL_HANDSHAKE *hs,
+                                               uint8_t *out_alert,
+                                               CBS *contents) {
+  if (contents == nullptr) {
+    return true;
+  }
+  if (CBS_len(contents) > 0) {
+    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+    return false;
+  }
+  hs->ech_is_inner_present = true;
+  return true;
+}
+
 
 // Renegotiation indication.
 //
@@ -1938,10 +1942,13 @@
   const SSL *const ssl = hs->ssl;
   if (hs->max_version < TLS1_3_VERSION || ssl->session == nullptr ||
       ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
-      // TODO(https://crbug.com/boringssl/275): Should we synthesize a
-      // placeholder PSK, at least when we offer early data? Otherwise
-      // ClientHelloOuter will contain an early_data extension without a
-      // pre_shared_key extension and potentially break the recovery flow.
+      // The ClientHelloOuter cannot include the PSK extension.
+      //
+      // TODO(https://crbug.com/boringssl/275): draft-ietf-tls-esni-10 mandates
+      // this, but it risks breaking the ClientHelloOuter flow on 0-RTT reject.
+      // Later drafts will recommend including a placeholder one, at which point
+      // we will need to synthesize a ticket. See
+      // https://github.com/tlswg/draft-ietf-tls-esni/issues/408
       type == ssl_client_hello_outer) {
     return false;
   }
@@ -1984,6 +1991,7 @@
 
   // Fill in a placeholder zero binder of the appropriate length. It will be
   // computed and filled in later after length prefixes are computed.
+  uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
   size_t binder_len = EVP_MD_size(ssl_session_get_digest(ssl->session.get()));
 
   CBB contents, identity, ticket, binders, binder;
@@ -1996,7 +2004,7 @@
       !CBB_add_u32(&identity, obfuscated_ticket_age) ||
       !CBB_add_u16_length_prefixed(&contents, &binders) ||
       !CBB_add_u8_length_prefixed(&binders, &binder) ||
-      !CBB_add_zeros(&binder, binder_len)) {
+      !CBB_add_bytes(&binder, zero_binder, binder_len)) {
     return false;
   }
 
@@ -2176,7 +2184,10 @@
   // If offering ECH, the extension only applies to ClientHelloInner, but we
   // send the extension in both ClientHellos. This ensures that, if the server
   // handshakes with ClientHelloOuter, it can skip past early data. See
-  // draft-ietf-tls-esni-13, section 6.1.
+  // https://github.com/tlswg/draft-ietf-tls-esni/pull/415
+  //
+  // TODO(https://crbug.com/boringssl/275): Replace this with a reference to the
+  // right section in the next draft.
   if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_early_data) ||
       !CBB_add_u16(out_compressible, 0) ||
       !CBB_flush(out_compressible)) {
@@ -3100,6 +3111,13 @@
     ext_ech_add_serverhello,
   },
   {
+    TLSEXT_TYPE_ech_is_inner,
+    ext_ech_is_inner_add_clienthello,
+    forbid_parse_serverhello,
+    ext_ech_is_inner_parse_clienthello,
+    dont_add_serverhello,
+  },
+  {
     TLSEXT_TYPE_extended_master_secret,
     ext_ems_add_clienthello,
     ext_ems_parse_serverhello,
@@ -3306,12 +3324,14 @@
 
 static bool add_padding_extension(CBB *cbb, uint16_t ext, size_t len) {
   CBB child;
+  uint8_t *ptr;
   if (!CBB_add_u16(cbb, ext) ||  //
       !CBB_add_u16_length_prefixed(cbb, &child) ||
-      !CBB_add_zeros(&child, len)) {
+      !CBB_add_space(&child, &ptr, len)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
   }
+  OPENSSL_memset(ptr, 0, len);
   return CBB_flush(cbb);
 }
 
@@ -3384,6 +3404,34 @@
     }
   }
 
+  // Pad the server name. See draft-ietf-tls-esni-10, section 6.1.2.
+  // TODO(https://crbug.com/boringssl/275): Ideally we'd pad the whole thing to
+  // reduce the output range. See
+  // https://github.com/tlswg/draft-ietf-tls-esni/issues/433
+  size_t padding_len = 0;
+  size_t maximum_name_length = hs->selected_ech_config->maximum_name_length;
+  if (ssl->hostname) {
+    size_t hostname_len = strlen(ssl->hostname.get());
+    if (hostname_len <= maximum_name_length) {
+      padding_len = maximum_name_length - hostname_len;
+    } else {
+      // If the server underestimated the maximum size, pad to a multiple of 32.
+      padding_len = 31 - (hostname_len - 1) % 32;
+      // If the input is close to |maximum_name_length|, pad to the next
+      // multiple for at least 32 bytes of length ambiguity.
+      if (hostname_len + padding_len < maximum_name_length + 32) {
+        padding_len += 32;
+      }
+    }
+  } else {
+    // No SNI. Pad up to |maximum_name_length|, including server_name extension
+    // overhead.
+    padding_len = 9 + maximum_name_length;
+  }
+  if (!add_padding_extension(&extensions, TLSEXT_TYPE_padding, padding_len)) {
+    return false;
+  }
+
   // Uncompressed extensions are encoded as-is.
   if (!CBB_add_bytes(&extensions_encoded, CBB_data(&extensions),
                      CBB_len(&extensions))) {
@@ -3425,8 +3473,8 @@
 
 bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded,
                                 bool *out_needs_psk_binder,
-                                ssl_client_hello_type_t type,
-                                size_t header_len) {
+                                ssl_client_hello_type_t type, size_t header_len,
+                                size_t omit_ech_len) {
   *out_needs_psk_binder = false;
 
   if (type == ssl_client_hello_inner) {
@@ -3459,14 +3507,20 @@
     size_t i = hs->extension_permutation.empty()
                    ? unpermuted
                    : hs->extension_permutation[unpermuted];
-    const size_t len_before = CBB_len(&extensions);
-    if (!kExtensions[i].add_clienthello(hs, &extensions, &extensions, type)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
-      ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
-      return false;
-    }
+    size_t bytes_written;
+    if (omit_ech_len != 0 &&
+        kExtensions[i].value == TLSEXT_TYPE_encrypted_client_hello) {
+      bytes_written = omit_ech_len;
+    } else {
+      const size_t len_before = CBB_len(&extensions);
+      if (!kExtensions[i].add_clienthello(hs, &extensions, &extensions, type)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
+        ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
+        return false;
+      }
 
-    const size_t bytes_written = CBB_len(&extensions) - len_before;
+      bytes_written = CBB_len(&extensions) - len_before;
+    }
     if (bytes_written != 0) {
       hs->extensions.sent |= (1u << i);
     }
@@ -3490,8 +3544,8 @@
   size_t psk_extension_len = ext_pre_shared_key_clienthello_length(hs, type);
   if (!SSL_is_dtls(ssl) && !ssl->quic_method &&
       !ssl->s3->used_hello_retry_request) {
-    header_len +=
-        SSL3_HM_HEADER_LENGTH + 2 + CBB_len(&extensions) + psk_extension_len;
+    header_len += SSL3_HM_HEADER_LENGTH + 2 + CBB_len(&extensions) +
+                  omit_ech_len + psk_extension_len;
     size_t padding_len = 0;
 
     // The final extension must be non-empty. WebSphere Application
@@ -3665,10 +3719,18 @@
   return true;
 }
 
-static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *cbs,
+static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs,
                                         int *out_alert) {
-  CBS extensions = *cbs;
-  if (!tls1_check_duplicate_extensions(&extensions)) {
+  SSL *const ssl = hs->ssl;
+  // Before TLS 1.3, ServerHello extensions blocks may be omitted if empty.
+  if (CBS_len(cbs) == 0 && ssl_protocol_version(ssl) < TLS1_3_VERSION) {
+    return true;
+  }
+
+  // Decode the extensions block and check it is valid.
+  CBS extensions;
+  if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
+      !tls1_check_duplicate_extensions(&extensions)) {
     *out_alert = SSL_AD_DECODE_ERROR;
     return false;
   }
@@ -3790,7 +3852,7 @@
   return true;
 }
 
-bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *cbs) {
+bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
   SSL *const ssl = hs->ssl;
   int alert = SSL_AD_DECODE_ERROR;
   if (!ssl_scan_serverhello_tlsext(hs, cbs, &alert)) {
@@ -3818,8 +3880,8 @@
     return ssl_ticket_aead_ignore_ticket;
   }
   // Split the ticket into the ticket and the MAC.
-  auto ticket_mac = ticket.last(mac_len);
-  ticket = ticket.first(ticket.size() - mac_len);
+  auto ticket_mac = ticket.subspan(ticket.size() - mac_len);
+  ticket = ticket.subspan(0, ticket.size() - mac_len);
   HMAC_Update(hmac_ctx, ticket.data(), ticket.size());
   HMAC_Final(hmac_ctx, mac, NULL);
   assert(mac_len == ticket_mac.size());
diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc
index fc85e21..db4ee71 100644
--- a/src/ssl/handshake.cc
+++ b/src/ssl/handshake.cc
@@ -126,7 +126,8 @@
 
 SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg)
     : ssl(ssl_arg),
-      ech_is_inner(false),
+      ech_present(false),
+      ech_is_inner_present(false),
       ech_authenticated_reject(false),
       scts_requested(false),
       handshake_finalized(false),
@@ -267,15 +268,12 @@
 }
 
 bool ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
-                          std::initializer_list<SSLExtension *> extensions,
+                          Span<const SSL_EXTENSION_TYPE> ext_types,
                           bool ignore_unknown) {
   // Reset everything.
-  for (SSLExtension *ext : extensions) {
-    ext->present = false;
-    CBS_init(&ext->data, nullptr, 0);
-    if (!ext->allowed) {
-      assert(!ignore_unknown);
-    }
+  for (const SSL_EXTENSION_TYPE &ext_type : ext_types) {
+    *ext_type.out_present = false;
+    CBS_init(ext_type.out_data, nullptr, 0);
   }
 
   CBS copy = *cbs;
@@ -289,10 +287,10 @@
       return false;
     }
 
-    SSLExtension *found = nullptr;
-    for (SSLExtension *ext : extensions) {
-      if (type == ext->type && ext->allowed) {
-        found = ext;
+    const SSL_EXTENSION_TYPE *found = nullptr;
+    for (const SSL_EXTENSION_TYPE &ext_type : ext_types) {
+      if (type == ext_type.type) {
+        found = &ext_type;
         break;
       }
     }
@@ -307,14 +305,14 @@
     }
 
     // Duplicate ext_types are forbidden.
-    if (found->present) {
+    if (*found->out_present) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
       return false;
     }
 
-    found->present = true;
-    found->data = data;
+    *found->out_present = 1;
+    *found->out_data = data;
   }
 
   return true;
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index 17b41e0..ba8f4b7 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -333,7 +333,8 @@
       !ssl_write_client_hello_without_extensions(hs, &body, type,
                                                  /*empty_session_id*/ false) ||
       !ssl_add_clienthello_tlsext(hs, &body, /*out_encoded=*/nullptr,
-                                  &needs_psk_binder, type, CBB_len(&body)) ||
+                                  &needs_psk_binder, type, CBB_len(&body),
+                                  /*omit_ech_len=*/0) ||
       !ssl->method->finish_message(ssl, cbb.get(), &msg)) {
     return false;
   }
@@ -353,31 +354,42 @@
   return ssl->method->add_message(ssl, std::move(msg));
 }
 
-static bool parse_server_version(const SSL_HANDSHAKE *hs, uint16_t *out_version,
-                                 uint8_t *out_alert,
-                                 const ParsedServerHello &server_hello) {
-  // If the outer version is not TLS 1.2, use it.
-  // TODO(davidben): This function doesn't quite match the RFC8446 formulation.
-  if (server_hello.legacy_version != TLS1_2_VERSION) {
-    *out_version = server_hello.legacy_version;
+static bool parse_supported_versions(SSL_HANDSHAKE *hs, uint16_t *version,
+                                     const CBS *in) {
+  // If the outer version is not TLS 1.2, or there is no extensions block, use
+  // the outer version.
+  if (*version != TLS1_2_VERSION || CBS_len(in) == 0) {
     return true;
   }
 
-  SSLExtension supported_versions(TLSEXT_TYPE_supported_versions);
-  CBS extensions = server_hello.extensions;
-  if (!ssl_parse_extensions(&extensions, out_alert, {&supported_versions},
-                            /*ignore_unknown=*/true)) {
+  SSL *const ssl = hs->ssl;
+  CBS copy = *in, extensions;
+  if (!CBS_get_u16_length_prefixed(&copy, &extensions) ||
+      CBS_len(&copy) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return false;
   }
 
-  if (!supported_versions.present) {
-    *out_version = server_hello.legacy_version;
-    return true;
+  bool have_supported_versions;
+  CBS supported_versions;
+  const SSL_EXTENSION_TYPE ext_types[] = {
+    {TLSEXT_TYPE_supported_versions, &have_supported_versions,
+     &supported_versions},
+  };
+
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+                            /*ignore_unknown=*/true)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return false;
   }
 
-  if (!CBS_get_u16(&supported_versions.data, out_version) ||
-       CBS_len(&supported_versions.data) != 0) {
-    *out_alert = SSL_AD_DECODE_ERROR;
+  // Override the outer version with the extension, if present.
+  if (have_supported_versions &&
+      (!CBS_get_u16(&supported_versions, version) ||
+       CBS_len(&supported_versions) != 0)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return false;
   }
 
@@ -433,7 +445,7 @@
 }
 
 void ssl_done_writing_client_hello(SSL_HANDSHAKE *hs) {
-  hs->ech_client_outer.Reset();
+  hs->ech_client_bytes.Reset();
   hs->cookie.Reset();
   hs->key_share_bytes.Reset();
 }
@@ -645,38 +657,6 @@
   return ssl_hs_flush;
 }
 
-bool ssl_parse_server_hello(ParsedServerHello *out, uint8_t *out_alert,
-                            const SSLMessage &msg) {
-  if (msg.type != SSL3_MT_SERVER_HELLO) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
-    return false;
-  }
-  out->raw = msg.raw;
-  CBS body = msg.body;
-  if (!CBS_get_u16(&body, &out->legacy_version) ||
-      !CBS_get_bytes(&body, &out->random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&body, &out->session_id) ||
-      CBS_len(&out->session_id) > SSL3_SESSION_ID_SIZE ||
-      !CBS_get_u16(&body, &out->cipher_suite) ||
-      !CBS_get_u8(&body, &out->compression_method)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return false;
-  }
-  // In TLS 1.2 and below, empty extensions blocks may be omitted. In TLS 1.3,
-  // ServerHellos always have extensions, so this can be applied generically.
-  CBS_init(&out->extensions, nullptr, 0);
-  if ((CBS_len(&body) != 0 &&
-       !CBS_get_u16_length_prefixed(&body, &out->extensions)) ||
-      CBS_len(&body) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return false;
-  }
-  return true;
-}
-
 static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   SSLMessage msg;
@@ -684,12 +664,26 @@
     return ssl_hs_read_server_hello;
   }
 
-  ParsedServerHello server_hello;
-  uint16_t server_version;
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_server_hello(&server_hello, &alert, msg) ||
-      !parse_server_version(hs, &server_version, &alert, server_hello)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
+    return ssl_hs_error;
+  }
+
+  CBS server_hello = msg.body, server_random, session_id;
+  uint16_t server_version, cipher_suite;
+  uint8_t compression_method;
+  if (!CBS_get_u16(&server_hello, &server_version) ||
+      !CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&server_hello, &session_id) ||
+      CBS_len(&session_id) > SSL3_SESSION_ID_SIZE ||
+      !CBS_get_u16(&server_hello, &cipher_suite) ||
+      !CBS_get_u8(&server_hello, &compression_method)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  // Use the supported_versions extension if applicable.
+  if (!parse_supported_versions(hs, &server_version, &server_hello)) {
     return ssl_hs_error;
   }
 
@@ -743,7 +737,7 @@
   }
 
   // Copy over the server random.
-  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random),
+  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
                  SSL3_RANDOM_SIZE);
 
   // Enforce the TLS 1.3 anti-downgrade feature.
@@ -766,26 +760,28 @@
     }
   }
 
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
+  if (cipher == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return ssl_hs_error;
+  }
+  hs->new_cipher = cipher;
+
   // The cipher must be allowed in the selected version and enabled.
-  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
   uint32_t mask_a, mask_k;
   ssl_get_client_disabled(hs, &mask_a, &mask_k);
-  if (cipher == nullptr ||
-      (cipher->algorithm_mkey & mask_k) ||
-      (cipher->algorithm_auth & mask_a) ||
+  if ((cipher->algorithm_mkey & mask_k) || (cipher->algorithm_auth & mask_a) ||
       SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) ||
-      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), nullptr, cipher)) {
+      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, cipher)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
-  hs->new_cipher = cipher;
-
   if (hs->session_id_len != 0 &&
-      CBS_mem_equal(&server_hello.session_id, hs->session_id,
-                    hs->session_id_len)) {
+      CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len)) {
     // Echoing the ClientHello session ID in TLS 1.2, whether from the session
     // or a synthetic one, indicates resumption. If there was no session (or if
     // the session was only offered in ECH ClientHelloInner), this was the
@@ -803,7 +799,7 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
-    if (ssl->session->cipher != hs->new_cipher) {
+    if (ssl->session->cipher != cipher) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
@@ -826,11 +822,10 @@
       return ssl_hs_error;
     }
     // Note: session_id could be empty.
-    hs->new_session->session_id_length = CBS_len(&server_hello.session_id);
-    OPENSSL_memcpy(hs->new_session->session_id,
-                   CBS_data(&server_hello.session_id),
-                   CBS_len(&server_hello.session_id));
-    hs->new_session->cipher = hs->new_cipher;
+    hs->new_session->session_id_length = CBS_len(&session_id);
+    OPENSSL_memcpy(hs->new_session->session_id, CBS_data(&session_id),
+                   CBS_len(&session_id));
+    hs->new_session->cipher = cipher;
   }
 
   // Now that the cipher is known, initialize the handshake hash and hash the
@@ -850,17 +845,26 @@
   }
 
   // Only the NULL compression algorithm is supported.
-  if (server_hello.compression_method != 0) {
+  if (compression_method != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
-  if (!ssl_parse_serverhello_tlsext(hs, &server_hello.extensions)) {
+  // TLS extensions
+  if (!ssl_parse_serverhello_tlsext(hs, &server_hello)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     return ssl_hs_error;
   }
 
+  // There should be nothing left over in the record.
+  if (CBS_len(&server_hello) != 0) {
+    // wrong packet length
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
   if (ssl->session != NULL &&
       hs->extended_master_secret != ssl->session->extended_master_secret) {
     if (ssl->session->extended_master_secret) {
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index fdf9511..c8a23a1 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -504,91 +504,6 @@
   return true;
 }
 
-static bool decrypt_ech(SSL_HANDSHAKE *hs, uint8_t *out_alert,
-                        const SSL_CLIENT_HELLO *client_hello) {
-  SSL *const ssl = hs->ssl;
-  CBS body;
-  if (!ssl_client_hello_get_extension(client_hello, &body,
-                                      TLSEXT_TYPE_encrypted_client_hello)) {
-    return true;
-  }
-  uint8_t type;
-  if (!CBS_get_u8(&body, &type)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return false;
-  }
-  if (type != ECH_CLIENT_OUTER) {
-    return true;
-  }
-  // This is a ClientHelloOuter ECH extension. Attempt to decrypt it.
-  uint8_t config_id;
-  uint16_t kdf_id, aead_id;
-  CBS enc, payload;
-  if (!CBS_get_u16(&body, &kdf_id) ||   //
-      !CBS_get_u16(&body, &aead_id) ||  //
-      !CBS_get_u8(&body, &config_id) ||
-      !CBS_get_u16_length_prefixed(&body, &enc) ||
-      !CBS_get_u16_length_prefixed(&body, &payload) ||  //
-      CBS_len(&body) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return false;
-  }
-
-  {
-    MutexReadLock lock(&ssl->ctx->lock);
-    hs->ech_keys = UpRef(ssl->ctx->ech_keys);
-  }
-
-  if (!hs->ech_keys) {
-    ssl->s3->ech_status = ssl_ech_rejected;
-    return true;
-  }
-
-  for (const auto &config : hs->ech_keys->configs) {
-    hs->ech_hpke_ctx.Reset();
-    if (config_id != config->ech_config().config_id ||
-        !config->SetupContext(hs->ech_hpke_ctx.get(), kdf_id, aead_id, enc)) {
-      // Ignore the error and try another ECHConfig.
-      ERR_clear_error();
-      continue;
-    }
-    Array<uint8_t> encoded_client_hello_inner;
-    bool is_decrypt_error;
-    if (!ssl_client_hello_decrypt(hs->ech_hpke_ctx.get(),
-                                  &encoded_client_hello_inner,
-                                  &is_decrypt_error, client_hello, payload)) {
-      if (is_decrypt_error) {
-        // Ignore the error and try another ECHConfig.
-        ERR_clear_error();
-        continue;
-      }
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
-      return false;
-    }
-
-    // Recover the ClientHelloInner from the EncodedClientHelloInner.
-    bssl::Array<uint8_t> client_hello_inner;
-    if (!ssl_decode_client_hello_inner(ssl, out_alert, &client_hello_inner,
-                                       encoded_client_hello_inner,
-                                       client_hello)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      return false;
-    }
-    hs->ech_client_hello_buf = std::move(client_hello_inner);
-    hs->ech_config_id = config_id;
-    ssl->s3->ech_status = ssl_ech_accepted;
-    return true;
-  }
-
-  // If we did not accept ECH, proceed with the ClientHelloOuter. Note this
-  // could be key mismatch or ECH GREASE, so we must complete the handshake
-  // as usual, except EncryptedExtensions will contain retry configs.
-  ssl->s3->ech_status = ssl_ech_rejected;
-  return true;
-}
-
 static bool extract_sni(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                         const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
@@ -668,19 +583,98 @@
     return ssl_hs_handoff;
   }
 
+  // If the ClientHello contains an encrypted_client_hello extension (and no
+  // ech_is_inner extension), act as a client-facing server and attempt to
+  // decrypt the ClientHelloInner.
+  CBS ech_body;
+  if (ssl_client_hello_get_extension(&client_hello, &ech_body,
+                                      TLSEXT_TYPE_encrypted_client_hello)) {
+    CBS unused;
+    if (ssl_client_hello_get_extension(&client_hello, &unused,
+                                       TLSEXT_TYPE_ech_is_inner)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      return ssl_hs_error;
+    }
+
+    // Parse a ClientECH out of the extension body.
+    uint8_t config_id;
+    uint16_t kdf_id, aead_id;
+    CBS enc, payload;
+    if (!CBS_get_u16(&ech_body, &kdf_id) ||  //
+        !CBS_get_u16(&ech_body, &aead_id) ||
+        !CBS_get_u8(&ech_body, &config_id) ||
+        !CBS_get_u16_length_prefixed(&ech_body, &enc) ||
+        !CBS_get_u16_length_prefixed(&ech_body, &payload) ||
+        CBS_len(&ech_body) != 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      return ssl_hs_error;
+    }
+
+    {
+      MutexReadLock lock(&ssl->ctx->lock);
+      hs->ech_keys = UpRef(ssl->ctx->ech_keys);
+    }
+
+    if (hs->ech_keys) {
+      for (const auto &config : hs->ech_keys->configs) {
+        hs->ech_hpke_ctx.Reset();
+        if (config_id != config->ech_config().config_id ||
+            !config->SetupContext(hs->ech_hpke_ctx.get(), kdf_id, aead_id,
+                                  enc)) {
+          // Ignore the error and try another ECHConfig.
+          ERR_clear_error();
+          continue;
+        }
+        Array<uint8_t> encoded_client_hello_inner;
+        bool is_decrypt_error;
+        if (!ssl_client_hello_decrypt(hs->ech_hpke_ctx.get(),
+                                      &encoded_client_hello_inner,
+                                      &is_decrypt_error, &client_hello, kdf_id,
+                                      aead_id, config_id, enc, payload)) {
+          if (is_decrypt_error) {
+            // Ignore the error and try another ECHConfig.
+            ERR_clear_error();
+            continue;
+          }
+          OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
+          return ssl_hs_error;
+        }
+
+        // Recover the ClientHelloInner from the EncodedClientHelloInner.
+        uint8_t alert = SSL_AD_DECODE_ERROR;
+        bssl::Array<uint8_t> client_hello_inner;
+        if (!ssl_decode_client_hello_inner(ssl, &alert, &client_hello_inner,
+                                           encoded_client_hello_inner,
+                                           &client_hello)) {
+          ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+          OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+          return ssl_hs_error;
+        }
+        hs->ech_client_hello_buf = std::move(client_hello_inner);
+
+        // Load the ClientHelloInner into |client_hello|.
+        if (!hs->GetClientHello(&msg, &client_hello)) {
+          OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+          return ssl_hs_error;
+        }
+
+        hs->ech_config_id = config_id;
+        ssl->s3->ech_status = ssl_ech_accepted;
+        break;
+      }
+    }
+
+    // If we did not accept ECH, proceed with the ClientHelloOuter. Note this
+    // could be key mismatch or ECH GREASE, so we most complete the handshake
+    // as usual, except EncryptedExtensions will contain retry configs.
+    if (ssl->s3->ech_status != ssl_ech_accepted) {
+      ssl->s3->ech_status = ssl_ech_rejected;
+    }
+  }
+
   uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!decrypt_ech(hs, &alert, &client_hello)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
-    return ssl_hs_error;
-  }
-
-  // ECH may have changed which ClientHello we process. Update |msg| and
-  // |client_hello| in case.
-  if (!hs->GetClientHello(&msg, &client_hello)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return ssl_hs_error;
-  }
-
   if (!extract_sni(hs, &alert, &client_hello)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
@@ -757,6 +751,12 @@
     return ssl_hs_error;
   }
 
+  if (hs->ech_present && hs->ech_is_inner_present) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return ssl_hs_error;
+  }
+
   hs->state = state12_select_certificate;
   return ssl_hs_ok;
 }
@@ -973,7 +973,8 @@
 }
 
 static void copy_suffix(Span<uint8_t> out, Span<const uint8_t> in) {
-  out = out.last(in.size());
+  out = out.subspan(out.size() - in.size());
+  assert(out.size() == in.size());
   OPENSSL_memcpy(out.data(), in.data(), in.size());
 }
 
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index ab23d29..3b7326a 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -146,7 +146,6 @@
 
 #include <stdlib.h>
 
-#include <initializer_list>
 #include <limits>
 #include <new>
 #include <type_traits>
@@ -694,8 +693,7 @@
   // InitHash initializes the handshake hash based on the PRF and contents of
   // the handshake transcript. Subsequent calls to |Update| will update the
   // rolling hash. It returns one on success and zero on failure. It is an error
-  // to call this function after the handshake buffer is released. This may be
-  // called multiple times to change the hash function.
+  // to call this function after the handshake buffer is released.
   bool InitHash(uint16_t version, const SSL_CIPHER *cipher);
 
   // UpdateForHelloRetryRequest resets the rolling hash with the
@@ -1451,7 +1449,7 @@
   Span<const uint8_t> public_name;
   Span<const uint8_t> cipher_suites;
   uint16_t kem_id = 0;
-  uint8_t maximum_name_length = 0;
+  uint16_t maximum_name_length = 0;
   uint8_t config_id = 0;
 };
 
@@ -1488,10 +1486,6 @@
   ssl_client_hello_outer,
 };
 
-// ECH_CLIENT_* are types for the ClientHello encrypted_client_hello extension.
-#define ECH_CLIENT_OUTER 0
-#define ECH_CLIENT_INNER 1
-
 // ssl_decode_client_hello_inner recovers the full ClientHelloInner from the
 // EncodedClientHelloInner |encoded_client_hello_inner| by replacing its
 // outer_extensions extension with the referenced extensions from the
@@ -1503,13 +1497,18 @@
     Span<const uint8_t> encoded_client_hello_inner,
     const SSL_CLIENT_HELLO *client_hello_outer);
 
-// ssl_client_hello_decrypt attempts to decrypt the |payload| and writes the
-// result to |*out|. |payload| must point into |client_hello_outer|. It returns
-// true on success and false on error. On error, it sets |*out_is_decrypt_error|
-// to whether the failure was due to a bad ciphertext.
-bool ssl_client_hello_decrypt(EVP_HPKE_CTX *hpke_ctx, Array<uint8_t> *out,
+// ssl_client_hello_decrypt attempts to decrypt the given |payload| into
+// |out_encoded_client_hello_inner|. The decrypted value should be an
+// EncodedClientHelloInner. It returns false if any fatal errors occur and true
+// otherwise, regardless of whether the decrypt was successful. It sets
+// |out_encoded_client_hello_inner| to true if the decryption fails, and false
+// otherwise.
+bool ssl_client_hello_decrypt(EVP_HPKE_CTX *hpke_ctx,
+                              Array<uint8_t> *out_encoded_client_hello_inner,
                               bool *out_is_decrypt_error,
                               const SSL_CLIENT_HELLO *client_hello_outer,
+                              uint16_t kdf_id, uint16_t aead_id,
+                              uint8_t config_id, Span<const uint8_t> enc,
                               Span<const uint8_t> payload);
 
 #define ECH_CONFIRMATION_SIGNAL_LEN 8
@@ -1519,14 +1518,13 @@
 size_t ssl_ech_confirmation_signal_hello_offset(const SSL *ssl);
 
 // ssl_ech_accept_confirmation computes the server's ECH acceptance signal,
-// writing it to |out|. The transcript portion is the concatenation of
-// |transcript| with |msg|. The |ECH_CONFIRMATION_SIGNAL_LEN| bytes from
-// |offset| in |msg| are replaced with zeros before hashing. This function
-// returns true on success, and false on failure.
+// writing it to |out|. The signal is computed by concatenating |transcript|
+// with |server_hello|. This function handles the fact that eight bytes of
+// |server_hello| need to be replaced with zeros before hashing. It returns true
+// on success, and false on failure.
 bool ssl_ech_accept_confirmation(const SSL_HANDSHAKE *hs, Span<uint8_t> out,
-                                 Span<const uint8_t> client_random,
-                                 const SSLTranscript &transcript, bool is_hrr,
-                                 Span<const uint8_t> msg, size_t offset);
+                                 const SSLTranscript &transcript,
+                                 Span<const uint8_t> server_hello);
 
 // ssl_is_valid_ech_public_name returns true if |public_name| is a valid ECH
 // public name and false otherwise. It is exported for testing.
@@ -1832,9 +1830,8 @@
   // cookie is the value of the cookie received from the server, if any.
   Array<uint8_t> cookie;
 
-  // ech_client_outer contains the outer ECH extension to send in the
-  // ClientHello, excluding the header and type byte.
-  Array<uint8_t> ech_client_outer;
+  // ech_client_bytes contains the ECH extension to send in the ClientHello.
+  Array<uint8_t> ech_client_bytes;
 
   // ech_retry_configs, on the client, contains the retry configs from the
   // server as a serialized ECHConfigList.
@@ -1942,9 +1939,13 @@
   // influence the handshake on match.
   UniquePtr<SSL_HANDSHAKE_HINTS> hints;
 
-  // ech_is_inner, on the server, indicates whether the ClientHello contained an
-  // inner ECH extension.
-  bool ech_is_inner : 1;
+  // ech_present, on the server, indicates whether the ClientHello contained an
+  // encrypted_client_hello extension.
+  bool ech_present : 1;
+
+  // ech_is_inner_present, on the server, indicates whether the ClientHello
+  // contained an ech_is_inner extension.
+  bool ech_is_inner_present : 1;
 
   // ech_authenticated_reject, on the client, indicates whether an ECH rejection
   // handshake has been authenticated.
@@ -2162,22 +2163,6 @@
 // flight. It returns true on success and false on error.
 bool ssl_add_client_hello(SSL_HANDSHAKE *hs);
 
-struct ParsedServerHello {
-  CBS raw;
-  uint16_t legacy_version = 0;
-  CBS random;
-  CBS session_id;
-  uint16_t cipher_suite = 0;
-  uint8_t compression_method = 0;
-  CBS extensions;
-};
-
-// ssl_parse_server_hello parses |msg| as a ServerHello. On success, it writes
-// the result to |*out| and returns true. Otherwise, it returns false and sets
-// |*out_alert| to an alert to send to the peer.
-bool ssl_parse_server_hello(ParsedServerHello *out, uint8_t *out_alert,
-                            const SSLMessage &msg);
-
 enum ssl_cert_verify_context_t {
   ssl_cert_verify_server,
   ssl_cert_verify_client,
@@ -2219,25 +2204,19 @@
 bool ssl_negotiate_alps(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                         const SSL_CLIENT_HELLO *client_hello);
 
-struct SSLExtension {
-  SSLExtension(uint16_t type_arg, bool allowed_arg = true)
-      : type(type_arg), allowed(allowed_arg), present(false) {
-    CBS_init(&data, nullptr, 0);
-  }
-
+struct SSL_EXTENSION_TYPE {
   uint16_t type;
-  bool allowed;
-  bool present;
-  CBS data;
+  bool *out_present;
+  CBS *out_data;
 };
 
 // ssl_parse_extensions parses a TLS extensions block out of |cbs| and advances
-// it. It writes the parsed extensions to pointers in |extensions|. On success,
-// it fills in the |present| and |data| fields and returns true. Otherwise, it
-// sets |*out_alert| to an alert to send and returns false. Unknown extensions
-// are rejected unless |ignore_unknown| is true.
+// it. It writes the parsed extensions to pointers denoted by |ext_types|. On
+// success, it fills in the |out_present| and |out_data| fields and returns
+// true. Otherwise, it sets |*out_alert| to an alert to send and returns false.
+// Unknown extensions are rejected unless |ignore_unknown| is true.
 bool ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
-                          std::initializer_list<SSLExtension *> extensions,
+                          Span<const SSL_EXTENSION_TYPE> ext_types,
                           bool ignore_unknown);
 
 // ssl_verify_peer_cert verifies the peer certificate for |hs|.
@@ -2276,9 +2255,6 @@
 OPENSSL_EXPORT bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out,
                                           Span<const uint8_t> body);
 
-bool ssl_parse_client_hello_with_trailing_data(const SSL *ssl, CBS *cbs,
-                                               SSL_CLIENT_HELLO *out);
-
 bool ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
                                     CBS *out, uint16_t extension_type);
 
@@ -2339,7 +2315,7 @@
 
 #define TLSEXT_CHANNEL_ID_SIZE 128
 
-// From RFC 4492, used in encoding the curve type in ECParameters
+// From RFC4492, used in encoding the curve type in ECParameters
 #define NAMED_CURVE_TYPE 3
 
 struct CERT {
@@ -3316,15 +3292,19 @@
 // ClientHello extension was the pre_shared_key extension and needs a PSK binder
 // filled in. The caller should then update |out| and, if applicable,
 // |out_encoded| with the binder after completing the whole message.
+//
+// If |omit_ech_len| is non-zero, the ECH extension is omitted, but padding is
+// computed as if there were an extension of length |omit_ech_len|. This is used
+// to compute ClientHelloOuterAAD.
 bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded,
                                 bool *out_needs_psk_binder,
-                                ssl_client_hello_type_t type,
-                                size_t header_len);
+                                ssl_client_hello_type_t type, size_t header_len,
+                                size_t omit_ech_len);
 
 bool ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out);
 bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
                                   const SSL_CLIENT_HELLO *client_hello);
-bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *extensions);
+bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs);
 
 #define tlsext_tick_md EVP_sha256
 
diff --git a/src/ssl/ssl_cipher.cc b/src/ssl/ssl_cipher.cc
index 60b3e2c..4f5049c 100644
--- a/src/ssl/ssl_cipher.cc
+++ b/src/ssl/ssl_cipher.cc
@@ -234,7 +234,7 @@
      SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
-    // GCM ciphersuites from RFC 5288
+    // GCM ciphersuites from RFC5288
 
     // Cipher 9C
     {
@@ -346,7 +346,7 @@
      SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
-    // GCM based TLS v1.2 ciphersuites from RFC 5289
+    // GCM based TLS v1.2 ciphersuites from RFC5289
 
     // Cipher C02B
     {
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index 03864e1..3c9fc90 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -1023,7 +1023,7 @@
 int SSL_peek(SSL *ssl, void *buf, int num) {
   if (ssl->quic_method != nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return -1;
+    return 0;
   }
 
   int ret = ssl_read_impl(ssl);
@@ -1044,7 +1044,7 @@
 
   if (ssl->quic_method != nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return -1;
+    return 0;
   }
 
   if (ssl->do_handshake == NULL) {
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 7ab5054..76f88c7 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -1675,8 +1675,8 @@
       return false;
     }
   }
-  if (!CBB_add_u8(&contents, params.max_name_len) ||
-      !CBB_add_u8_length_prefixed(&contents, &child) ||
+  if (!CBB_add_u16(&contents, params.max_name_len) ||
+      !CBB_add_u16_length_prefixed(&contents, &child) ||
       !CBB_add_bytes(
           &child, reinterpret_cast<const uint8_t *>(params.public_name.data()),
           params.public_name.size()) ||
@@ -1735,9 +1735,9 @@
 
   static const uint8_t kECHConfig[] = {
       // version
-      0xfe, 0x0d,
+      0xfe, 0x0a,
       // length
-      0x00, 0x41,
+      0x00, 0x43,
       // contents.config_id
       0x01,
       // contents.kem_id
@@ -1749,10 +1749,10 @@
       // contents.cipher_suites
       0x00, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03,
       // contents.maximum_name_length
-      0x10,
+      0x00, 0x10,
       // contents.public_name
-      0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d,
-      0x70, 0x6c, 0x65,
+      0x00, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2e, 0x65, 0x78, 0x61,
+      0x6d, 0x70, 0x6c, 0x65,
       // contents.extensions
       0x00, 0x00};
   uint8_t *ech_config;
@@ -2069,26 +2069,20 @@
   EXPECT_EQ(client_hello_len, client_hello_len_baseline);
   EXPECT_EQ(ech_len, ech_len_baseline);
 
-  // Name lengths above the maximum do not get named-based padding, but the
-  // overall input is padded to a multiple of 32.
-  size_t client_hello_len_baseline2, ech_len_baseline2;
-  ASSERT_TRUE(GetECHLength(ctx.get(), &client_hello_len_baseline2,
-                           &ech_len_baseline2, 128,
-                           std::string(128 + 32, 'a').c_str()));
-  EXPECT_EQ(ech_len_baseline2, ech_len_baseline + 32);
-  // The ClientHello lengths may match if we are still under the threshold for
-  // padding extension.
-  EXPECT_GE(client_hello_len_baseline2, client_hello_len_baseline);
+  size_t client_hello_len_129, ech_len_129;
+  ASSERT_TRUE(GetECHLength(ctx.get(), &client_hello_len_129, &ech_len_129, 128,
+                           std::string(129, 'a').c_str()));
+  // The padding calculation should not pad beyond the maximum.
+  EXPECT_GT(ech_len_129, ech_len_baseline);
 
-  for (size_t name_len = 128 + 1; name_len < 128 + 32; name_len++) {
+  // If the SNI exceeds the maximum name length, we apply some generic padding,
+  // so close name lengths still match.
+  for (size_t name_len : {129, 130, 131, 132}) {
     SCOPED_TRACE(name_len);
     ASSERT_TRUE(GetECHLength(ctx.get(), &client_hello_len, &ech_len, 128,
                              std::string(name_len, 'a').c_str()));
-    EXPECT_TRUE(ech_len == ech_len_baseline || ech_len == ech_len_baseline2)
-        << ech_len;
-    EXPECT_TRUE(client_hello_len == client_hello_len_baseline ||
-                client_hello_len == client_hello_len_baseline2)
-        << client_hello_len;
+    EXPECT_EQ(client_hello_len, client_hello_len_129);
+    EXPECT_EQ(ech_len, ech_len_129);
   }
 }
 
@@ -2119,39 +2113,44 @@
   EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span(
       "abcdefhijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234567899")));
 
-  // Inputs with trailing numeric components are rejected.
+  // Inputs that parse as IPv4 addresses are rejected.
   EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("127.0.0.1")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("example.1")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("example.01")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("example.0x01")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("example.0X01")));
-  // Leading zeros and values that overflow |uint32_t| are still rejected.
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(
-      str_to_span("example.123456789000000000000000")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(
-      str_to_span("example.012345678900000000000000")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(
-      str_to_span("example.0x123456789abcdefABCDEF0")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(
-      str_to_span("example.0x0123456789abcdefABCDEF")));
-  // Adding a non-digit or non-hex character makes it a valid DNS name again.
-  // Single-component numbers are rejected.
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0177.0.0.01")));
+  EXPECT_FALSE(
+      ssl_is_valid_ech_public_name(str_to_span("0x7f.0x.0x.0x00000001")));
+  EXPECT_FALSE(
+      ssl_is_valid_ech_public_name(str_to_span("0XAB.0XCD.0XEF.0X01")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0.0.0.0")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("255.255.255.255")));
+  // Out-of-bounds or overflowing components are not IP addresses.
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("256.255.255.255")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("255.0x100.255.255")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("255.255.255.0400")));
   EXPECT_TRUE(ssl_is_valid_ech_public_name(
-      str_to_span("example.1234567890a")));
-  EXPECT_TRUE(ssl_is_valid_ech_public_name(
-      str_to_span("example.01234567890a")));
-  EXPECT_TRUE(ssl_is_valid_ech_public_name(
-      str_to_span("example.0x123456789abcdefg")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("1")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("01")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0x01")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0X01")));
-  // Numbers with trailing dots are rejected. (They are already rejected by the
-  // LDH label rules, but the WHATWG URL parser additionally rejects them.)
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("1.")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("01.")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0x01.")));
-  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0X01.")));
+      str_to_span("255.255.255.0x100000000")));
+  // Invalid characters for the base are not IP addresses.
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("12a.0.0.1")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0xg.0.0.1")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("08.0.0.1")));
+  // Trailing components can be merged into a single component.
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("127.0.1")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("127.1")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("2130706433")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0x7f000001")));
+  // Merged components must respect their limits.
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0.0.0.0xff")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0.0.0xffff")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0.0xffffff")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0xffffffff")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0.0.0.0x100")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0.0.0x10000")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0.0x1000000")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0x100000000")));
+  // Correctly handle overflow in decimal and octal.
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("037777777777")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("040000000000")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("4294967295")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("4294967296")));
 }
 
 // When using the built-in verifier, test that |SSL_get0_ech_name_override| is
diff --git a/src/ssl/ssl_transcript.cc b/src/ssl/ssl_transcript.cc
index 58fd21e..1599c80 100644
--- a/src/ssl/ssl_transcript.cc
+++ b/src/ssl/ssl_transcript.cc
@@ -158,14 +158,20 @@
   return true;
 }
 
+// InitDigestWithData calls |EVP_DigestInit_ex| on |ctx| with |md| and then
+// writes the data in |buf| to it.
+static bool InitDigestWithData(EVP_MD_CTX *ctx, const EVP_MD *md,
+                               const BUF_MEM *buf) {
+  if (!EVP_DigestInit_ex(ctx, md, NULL)) {
+    return false;
+  }
+  EVP_DigestUpdate(ctx, buf->data, buf->length);
+  return true;
+}
+
 bool SSLTranscript::InitHash(uint16_t version, const SSL_CIPHER *cipher) {
   const EVP_MD *md = ssl_get_handshake_digest(version, cipher);
-  if (Digest() == md) {
-    // No need to re-hash the buffer.
-    return true;
-  }
-  return EVP_DigestInit_ex(hash_.get(), md, nullptr) &&
-         EVP_DigestUpdate(hash_.get(), buffer_->data, buffer_->length);
+  return InitDigestWithData(hash_.get(), md, buffer_.get());
 }
 
 void SSLTranscript::FreeBuffer() {
diff --git a/src/ssl/ssl_x509.cc b/src/ssl/ssl_x509.cc
index 680f253..98f1f6a 100644
--- a/src/ssl/ssl_x509.cc
+++ b/src/ssl/ssl_x509.cc
@@ -379,9 +379,8 @@
   const char *name;
   size_t name_len;
   SSL_get0_ech_name_override(ssl, &name, &name_len);
-  UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new());
-  if (!ctx ||
-      !X509_STORE_CTX_init(ctx.get(), verify_store, leaf, cert_chain) ||
+  ScopedX509_STORE_CTX ctx;
+  if (!X509_STORE_CTX_init(ctx.get(), verify_store, leaf, cert_chain) ||
       !X509_STORE_CTX_set_ex_data(ctx.get(),
                                   SSL_get_ex_data_X509_STORE_CTX_idx(), ssl) ||
       // We need to inherit the verify parameters. These can be determined by
@@ -412,11 +411,11 @@
     verify_ret = X509_verify_cert(ctx.get());
   }
 
-  session->verify_result = X509_STORE_CTX_get_error(ctx.get());
+  session->verify_result = ctx->error;
 
   // If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.
   if (verify_ret <= 0 && hs->config->verify_mode != SSL_VERIFY_NONE) {
-    *out_alert = SSL_alert_from_verify_result(session->verify_result);
+    *out_alert = SSL_alert_from_verify_result(ctx->error);
     return false;
   }
 
@@ -465,9 +464,9 @@
     return false;
   }
 
-  UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new());
-  if (!ctx || !X509_STORE_CTX_init(ctx.get(), hs->ssl->ctx->cert_store,
-                                   leaf.get(), nullptr)) {
+  ScopedX509_STORE_CTX ctx;
+  if (!X509_STORE_CTX_init(ctx.get(), hs->ssl->ctx->cert_store, leaf.get(),
+                           NULL)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return false;
   }
@@ -477,13 +476,9 @@
   ERR_clear_error();
 
   // Remove the leaf from the generated chain.
-  UniquePtr<STACK_OF(X509)> chain(X509_STORE_CTX_get1_chain(ctx.get()));
-  if (!chain) {
-    return false;
-  }
-  X509_free(sk_X509_shift(chain.get()));
+  X509_free(sk_X509_shift(ctx->chain));
 
-  if (!ssl_cert_set_chain(hs->config->cert.get(), chain.get())) {
+  if (!ssl_cert_set_chain(hs->config->cert.get(), ctx->chain)) {
     return false;
   }
 
@@ -711,6 +706,13 @@
   return X509_STORE_load_locations(ctx->cert_store, ca_file, ca_dir);
 }
 
+void SSL_set_verify_result(SSL *ssl, long result) {
+  check_ssl_x509_method(ssl);
+  if (result != X509_V_OK) {
+    abort();
+  }
+}
+
 long SSL_get_verify_result(const SSL *ssl) {
   check_ssl_x509_method(ssl);
   SSL_SESSION *session = SSL_get_session(ssl);
diff --git a/src/ssl/test/fuzzer.h b/src/ssl/test/fuzzer.h
index 00b5e84..509cfdb 100644
--- a/src/ssl/test/fuzzer.h
+++ b/src/ssl/test/fuzzer.h
@@ -231,6 +231,16 @@
     0x01, 'a', 0x02, 'a', 'a', 0x03, 'a', 'a', 'a',
 };
 
+const uint8_t kECHConfig[] = {
+    0xfe, 0x0a, 0x00, 0x47, 0x2a, 0x00, 0x20, 0x00, 0x20, 0x6c, 0x55,
+    0x96, 0x41, 0x3d, 0x12, 0x4e, 0x63, 0x3d, 0x39, 0x7a, 0xe9, 0xbc,
+    0xec, 0xb2, 0x55, 0xd0, 0xe6, 0xaa, 0xbd, 0xa9, 0x79, 0xb8, 0x86,
+    0x9a, 0x13, 0x61, 0xc6, 0x69, 0xac, 0xb4, 0x21, 0x00, 0x0c, 0x00,
+    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03,
+    0x00, 0x10, 0x00, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2e,
+    0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 0x00,
+};
+
 const uint8_t kECHKey[] = {
     0x35, 0x6d, 0x45, 0x06, 0xb3, 0x88, 0x89, 0x2e, 0xd6, 0x87, 0x84,
     0xd2, 0x2d, 0x6f, 0x83, 0x48, 0xad, 0xf2, 0xfd, 0x08, 0x51, 0x73,
@@ -448,20 +458,11 @@
     if (role_ == kServer) {
       bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
       bssl::ScopedEVP_HPKE_KEY key;
-      uint8_t *ech_config;
-      size_t ech_config_len;
       if (!keys ||
           !EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(), kECHKey,
                              sizeof(kECHKey)) ||
-          // Match |echConfig| in |addEncryptedClientHelloTests| from runner.go.
-          !SSL_marshal_ech_config(&ech_config, &ech_config_len,
-                                  /*config_id=*/42, key.get(), "public.example",
-                                  /*max_name_len=*/64)) {
-        return false;
-      }
-      bssl::UniquePtr<uint8_t> free_ech_config(ech_config);
-      if (!SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/true, ech_config,
-                            ech_config_len, key.get()) ||
+          !SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/true, kECHConfig,
+                            sizeof(kECHConfig), key.get()) ||
           !SSL_CTX_set1_ech_keys(ctx_.get(), keys.get())) {
         return false;
       }
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index bf6a3d1..782eb36 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -128,7 +128,8 @@
 	extensionChannelID                  uint16 = 30032  // not IANA assigned
 	extensionDelegatedCredentials       uint16 = 0x22   // draft-ietf-tls-subcerts-06
 	extensionDuplicate                  uint16 = 0xffff // not IANA assigned
-	extensionEncryptedClientHello       uint16 = 0xfe0d // not IANA assigned
+	extensionEncryptedClientHello       uint16 = 0xfe0a // not IANA assigned
+	extensionECHIsInner                 uint16 = 0xda09 // not IANA assigned
 	extensionECHOuterExtensions         uint16 = 0xfd00 // not IANA assigned
 )
 
@@ -175,7 +176,7 @@
 	CertTypeRSAFixedDH = 3 // A certificate containing a static DH key
 	CertTypeDSSFixedDH = 4 // A certificate containing a static DH key
 
-	// See RFC 4492 sections 3 and 5.5.
+	// See RFC4492 sections 3 and 5.5.
 	CertTypeECDSASign      = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA.
 	CertTypeRSAFixedECDH   = 65 // A certificate containing an ECDH-capable public key, signed with RSA.
 	CertTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA.
@@ -242,9 +243,6 @@
 	keyUpdateRequested    = 1
 )
 
-// draft-ietf-tls-esni-13, sections 7.2 and 7.2.1.
-const echAcceptConfirmationLength = 8
-
 // ConnectionState records basic TLS details about the connection.
 type ConnectionState struct {
 	Version                    uint16                // TLS version used by the connection (e.g. VersionTLS12)
@@ -871,53 +869,40 @@
 	// retry configs.
 	SendECHRetryConfigs []byte
 
-	// AlwaysSendECHRetryConfigs, if true, causes the ECH server to send retry
-	// configs unconditionally, including in the TLS 1.2 ServerHello.
-	AlwaysSendECHRetryConfigs bool
+	// SendECHRetryConfigsInTLS12ServerHello, if true, causes the ECH server to
+	// send retry configs in the TLS 1.2 ServerHello.
+	SendECHRetryConfigsInTLS12ServerHello bool
 
-	// AlwaysSendECHHelloRetryRequest, if true, causes the ECH server to send
-	// the ECH HelloRetryRequest extension unconditionally.
-	AlwaysSendECHHelloRetryRequest bool
+	// SendInvalidECHIsInner, if not empty, causes the client to send the
+	// specified byte string in the ech_is_inner extension.
+	SendInvalidECHIsInner []byte
 
-	// SendInvalidECHInner, if not empty, causes the client to send the
-	// specified byte string after the type field in ClientHelloInner
-	// encrypted_client_hello extension.
-	SendInvalidECHInner []byte
-
-	// OmitECHInner, if true, causes the client to omit the encrypted_client_hello
+	// OmitECHIsInner, if true, causes the client to omit the ech_is_inner
 	// extension on the ClientHelloInner message.
-	OmitECHInner bool
+	OmitECHIsInner bool
 
-	// OmitSecondECHInner, if true, causes the client to omit the
-	// encrypted_client_hello extension on the second ClientHelloInner message.
-	OmitSecondECHInner bool
+	// OmitSecondECHIsInner, if true, causes the client to omit the ech_is_inner
+	// extension on the second ClientHelloInner message.
+	OmitSecondECHIsInner bool
 
-	// OmitServerHelloECHConfirmation, if true, causes the server to omit the
-	// ECH confirmation in the ServerHello.
-	OmitServerHelloECHConfirmation bool
-
-	// AlwaysSendECHInner, if true, causes the client to send an inner
-	// encrypted_client_hello extension on all ClientHello messages. The server
-	// is then expected to unconditionally confirm the extension when
-	// negotiating TLS 1.3 or later.
-	AlwaysSendECHInner bool
+	// AlwaysSendECHIsInner, if true, causes the client to send the
+	// ech_is_inner extension on all ClientHello messages. The server is then
+	// expected to unconditionally confirm the extension when negotiating
+	// TLS 1.3 or later.
+	AlwaysSendECHIsInner bool
 
 	// TruncateClientECHEnc, if true, causes the client to send a shortened
 	// ClientECH.enc value in its encrypted_client_hello extension.
 	TruncateClientECHEnc bool
 
-	// ClientECHPadding is the number of bytes of padding to add to the client
-	// ECH payload.
-	ClientECHPadding int
-
-	// BadClientECHPadding, if true, causes the client ECH padding to contain a
-	// non-zero byte.
-	BadClientECHPadding bool
-
 	// OfferSessionInClientHelloOuter, if true, causes the client to offer
 	// sessions in ClientHelloOuter.
 	OfferSessionInClientHelloOuter bool
 
+	// FirstExtensionInClientHelloOuter, if non-zero, causes the client to place
+	// the specified extension first in ClientHelloOuter.
+	FirstExtensionInClientHelloOuter uint16
+
 	// OnlyCompressSecondClientHelloInner, if true, causes the client to
 	// only apply outer_extensions to the second ClientHello.
 	OnlyCompressSecondClientHelloInner bool
@@ -956,11 +941,6 @@
 	// will require to be omitted in ech_outer_extensions.
 	ExpectECHUncompressedExtensions []uint16
 
-	// ECHOuterExtensionOrder, if not nil, is an extension order to apply to
-	// ClientHelloOuter, instead of ordering the |ECHOuterExtensions| to match
-	// in both ClientHellos.
-	ECHOuterExtensionOrder []uint16
-
 	// UseInnerSessionWithClientHelloOuter, if true, causes the server to
 	// handshake with ClientHelloOuter, but resume the session from
 	// ClientHelloInner.
@@ -1031,10 +1011,6 @@
 	// normally expected to look ahead for ChangeCipherSpec.)
 	EmptyTicketSessionID bool
 
-	// NewSessionIDLength, if non-zero is the length of the session ID to use
-	// when issung new sessions.
-	NewSessionIDLength int
-
 	// SendClientHelloSessionID, if not nil, is the session ID sent in the
 	// ClientHello.
 	SendClientHelloSessionID []byte
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 5d04994..424b206 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -26,18 +26,19 @@
 const echBadPayloadByte = 0xff
 
 type clientHandshakeState struct {
-	c              *Conn
-	serverHello    *serverHelloMsg
-	hello          *clientHelloMsg
-	innerHello     *clientHelloMsg
-	echHPKEContext *hpke.Context
-	suite          *cipherSuite
-	finishedHash   finishedHash
-	keyShares      map[CurveID]ecdhCurve
-	masterSecret   []byte
-	session        *ClientSessionState
-	finishedBytes  []byte
-	peerPublicKey  crypto.PublicKey
+	c                 *Conn
+	serverHello       *serverHelloMsg
+	hello             *clientHelloMsg
+	innerHello        *clientHelloMsg
+	echHPKEContext    *hpke.Context
+	suite             *cipherSuite
+	finishedHash      finishedHash
+	innerFinishedHash finishedHash
+	keyShares         map[CurveID]ecdhCurve
+	masterSecret      []byte
+	session           *ClientSessionState
+	finishedBytes     []byte
+	peerPublicKey     crypto.PublicKey
 }
 
 func mapClientHelloVersion(vers uint16, isDTLS bool) uint16 {
@@ -339,6 +340,10 @@
 
 	hs.finishedHash = newFinishedHash(c.wireVersion, c.isDTLS, hs.suite)
 	hs.finishedHash.WriteHandshake(hs.hello.marshal(), hs.c.sendHandshakeSeq-1)
+	if hs.innerHello != nil {
+		hs.innerFinishedHash = newFinishedHash(c.wireVersion, c.isDTLS, hs.suite)
+		hs.innerFinishedHash.WriteHandshake(hs.innerHello.marshal(), hs.c.sendHandshakeSeq-1)
+	}
 
 	if c.vers >= VersionTLS13 {
 		if err := hs.doTLS13Handshake(msg); err != nil {
@@ -528,6 +533,9 @@
 	// list of prefix extensions. The marshal function will try these
 	// extensions before any others, followed by any remaining extensions in
 	// the default order.
+	if innerHello != nil && c.config.Bugs.FirstExtensionInClientHelloOuter != 0 {
+		hello.prefixExtensions = append(hello.prefixExtensions, c.config.Bugs.FirstExtensionInClientHelloOuter)
+	}
 	if c.config.Bugs.PSKBinderFirst && !c.config.Bugs.OnlyCorruptSecondPSKBinder {
 		hello.prefixExtensions = append(hello.prefixExtensions, extensionPreSharedKey)
 	}
@@ -535,24 +543,8 @@
 		hello.prefixExtensions = append(hello.prefixExtensions, extensionALPN)
 		hello.prefixExtensions = append(hello.prefixExtensions, extensionNextProtoNeg)
 	}
-
-	// Configure ech_outer_extensions.
-	if isInner {
-		hello.outerExtensions = c.config.ECHOuterExtensions
-		// If |OnlyCompressSecondClientHelloInner| is set, we still configure
-		// |hello.outerExtensions| for ordering, so that we do not introduce an
-		// unsolicited change across HelloRetryRequest.
-		hello.reorderOuterExtensionsWithoutCompressing = c.config.Bugs.OnlyCompressSecondClientHelloInner
-	} else {
-		// Compressed extensions must appear in the same relative order between
-		// ClientHelloInner and ClientHelloOuter. For simplicity, we default to
-		// forcing their order to match, but the caller can override this with
-		// either valid or invalid explicit orders.
-		if c.config.Bugs.ECHOuterExtensionOrder != nil {
-			hello.prefixExtensions = append(hello.prefixExtensions, c.config.Bugs.ECHOuterExtensionOrder...)
-		} else {
-			hello.prefixExtensions = append(hello.prefixExtensions, c.config.ECHOuterExtensions...)
-		}
+	if isInner && len(c.config.ECHOuterExtensions) > 0 && !c.config.Bugs.OnlyCompressSecondClientHelloInner {
+		applyECHOuterExtensions(hello, c.config.ECHOuterExtensions)
 	}
 
 	if maxVersion >= VersionTLS13 {
@@ -824,9 +816,9 @@
 		hello.hasEarlyData = innerHello.hasEarlyData
 	}
 
-	if (isInner && !c.config.Bugs.OmitECHInner) || c.config.Bugs.AlwaysSendECHInner {
-		hello.echInner = true
-		hello.invalidECHInner = c.config.Bugs.SendInvalidECHInner
+	if (isInner && !c.config.Bugs.OmitECHIsInner) || c.config.Bugs.AlwaysSendECHIsInner {
+		hello.echIsInner = true
+		hello.invalidECHIsInner = c.config.Bugs.SendInvalidECHIsInner
 	}
 
 	if innerHello != nil {
@@ -835,9 +827,9 @@
 		}
 		if c.config.Bugs.CorruptEncryptedClientHello {
 			if c.config.Bugs.NullAllCiphers {
-				hello.echOuter.payload = []byte{echBadPayloadByte}
+				hello.clientECH.payload = []byte{echBadPayloadByte}
 			} else {
-				hello.echOuter.payload[0] ^= 1
+				hello.clientECH.payload[0] ^= 1
 			}
 		}
 	}
@@ -886,31 +878,27 @@
 		enc = enc[:1]
 	}
 
-	encodedInner := innerHello.marshalForEncodedInner()
-	padding := make([]byte, c.config.Bugs.ClientECHPadding)
-	if c.config.Bugs.BadClientECHPadding {
-		padding[0] = 1
-	}
-	encodedInner = append(encodedInner, padding...)
+	aad := newByteBuilder()
+	aad.addU16(hs.echHPKEContext.KDF())
+	aad.addU16(hs.echHPKEContext.AEAD())
+	aad.addU8(configID)
+	aad.addU16LengthPrefixed().addBytes(enc)
+	hello.marshalForOuterAAD(aad.addU24LengthPrefixed())
 
-	// Encode ClientHelloOuter with a placeholder payload string.
-	payloadLength := len(encodedInner)
-	if !c.config.Bugs.NullAllCiphers {
-		payloadLength += hs.echHPKEContext.Overhead()
+	encodedInner := innerHello.marshalForEncodedInner()
+	payload := hs.echHPKEContext.Seal(encodedInner, aad.finish())
+
+	if c.config.Bugs.NullAllCiphers {
+		payload = encodedInner
 	}
-	hello.echOuter = &echClientOuter{
+
+	// Place the ECH extension in the outer CH.
+	hello.clientECH = &clientECH{
 		kdfID:    hs.echHPKEContext.KDF(),
 		aeadID:   hs.echHPKEContext.AEAD(),
 		configID: configID,
 		enc:      enc,
-		payload:  make([]byte, payloadLength),
-	}
-	aad := hello.marshal()[4:] // Remove message header
-
-	hello.raw = nil
-	hello.echOuter.payload = hs.echHPKEContext.Seal(encodedInner, aad)
-	if c.config.Bugs.NullAllCiphers {
-		hello.echOuter.payload = encodedInner
+		payload:  payload,
 	}
 
 	if c.config.Bugs.RecordClientHelloInner != nil {
@@ -925,73 +913,24 @@
 	return nil
 }
 
-func (hs *clientHandshakeState) checkECHConfirmation(msg interface{}, hello *clientHelloMsg, finishedHash *finishedHash) bool {
-	var offset int
-	var raw, label []byte
-	if hrr, ok := msg.(*helloRetryRequestMsg); ok {
-		if hrr.echConfirmationOffset == 0 {
-			return false
-		}
-		raw = hrr.raw
-		label = echAcceptConfirmationHRRLabel
-		offset = hrr.echConfirmationOffset
-	} else {
-		raw = msg.(*serverHelloMsg).raw
-		label = echAcceptConfirmationLabel
-		offset = 4 + 2 + 32 - echAcceptConfirmationLength
-	}
-
-	withZeros := append(make([]byte, 0, len(raw)), raw...)
-	for i := 0; i < echAcceptConfirmationLength; i++ {
-		withZeros[i+offset] = 0
-	}
-
-	confirmation := finishedHash.echAcceptConfirmation(hello.random, label, withZeros)
-	return bytes.Equal(confirmation, raw[offset:offset+echAcceptConfirmationLength])
-}
-
 func (hs *clientHandshakeState) doTLS13Handshake(msg interface{}) error {
 	c := hs.c
 
-	// The first message may be a ServerHello or HelloRetryRequest.
-	helloRetryRequest, haveHelloRetryRequest := msg.(*helloRetryRequestMsg)
-	if haveHelloRetryRequest {
-		hs.finishedHash.UpdateForHelloRetryRequest()
-	}
-
-	// Determine whether the server accepted ECH and drop the unnecessary
-	// transcript.
-	if hs.innerHello != nil {
-		innerFinishedHash := newFinishedHash(c.wireVersion, c.isDTLS, hs.suite)
-		innerFinishedHash.WriteHandshake(hs.innerHello.marshal(), hs.c.sendHandshakeSeq-1)
-		if haveHelloRetryRequest {
-			innerFinishedHash.UpdateForHelloRetryRequest()
-		}
-		if hs.checkECHConfirmation(msg, hs.innerHello, &innerFinishedHash) {
-			c.echAccepted = true
-			// Replace the transcript. For now, leave hs.hello and hs.innerHello
-			// as-is. HelloRetryRequest requires both be available.
-			hs.finishedHash = innerFinishedHash
-		}
-	} else {
-		// When not offering ECH, test that the backend server does not (or does)
-		// send a confirmation as expected.
-		confirmed := hs.checkECHConfirmation(msg, hs.hello, &hs.finishedHash)
-		if hs.hello.echInner && !confirmed {
-			return fmt.Errorf("tls: server did not send ECH confirmation in %T when requested", msg)
-		} else if !hs.hello.echInner && confirmed {
-			return fmt.Errorf("tls: server sent ECH confirmation in %T when not requested", msg)
-		}
-	}
-
 	// Once the PRF hash is known, TLS 1.3 does not require a handshake buffer.
 	hs.finishedHash.discardHandshakeBuffer()
 
 	// The first server message must be followed by a ChangeCipherSpec.
 	c.expectTLS13ChangeCipherSpec = true
 
+	// The first message may be a ServerHello or HelloRetryRequest.
+	helloRetryRequest, haveHelloRetryRequest := msg.(*helloRetryRequestMsg)
 	if haveHelloRetryRequest {
+		hs.finishedHash.UpdateForHelloRetryRequest()
 		hs.writeServerHash(helloRetryRequest.marshal())
+		if hs.innerHello != nil {
+			hs.innerFinishedHash.UpdateForHelloRetryRequest()
+			hs.innerFinishedHash.WriteHandshake(helloRetryRequest.marshal(), c.recvHandshakeSeq-1)
+		}
 
 		if c.config.Bugs.FailIfHelloRetryRequested {
 			return errors.New("tls: unexpected HelloRetryRequest")
@@ -1005,17 +944,20 @@
 		// Reset the encryption state, in case we sent 0-RTT data.
 		c.out.resetCipher()
 
-		if c.echAccepted {
-			if err := hs.applyHelloRetryRequest(helloRetryRequest, hs.innerHello, hs.hello); err != nil {
+		if hs.innerHello != nil {
+			if err := hs.applyHelloRetryRequest(helloRetryRequest, hs.innerHello, nil); err != nil {
 				return err
 			}
-			hs.writeClientHash(hs.innerHello.marshal())
+			if err := hs.applyHelloRetryRequest(helloRetryRequest, hs.hello, hs.innerHello); err != nil {
+				return err
+			}
+			hs.innerFinishedHash.WriteHandshake(hs.innerHello.marshal(), c.sendHandshakeSeq)
 		} else {
 			if err := hs.applyHelloRetryRequest(helloRetryRequest, hs.hello, nil); err != nil {
 				return err
 			}
-			hs.writeClientHash(hs.hello.marshal())
 		}
+		hs.writeClientHash(hs.hello.marshal())
 		toWrite := hs.hello.marshal()
 
 		if c.config.Bugs.PartialSecondClientHelloAfterFirst {
@@ -1048,12 +990,6 @@
 		}
 	}
 
-	// We no longer need to retain two ClientHellos.
-	if c.echAccepted {
-		hs.hello = hs.innerHello
-	}
-	hs.innerHello = nil
-
 	var ok bool
 	hs.serverHello, ok = msg.(*serverHelloMsg)
 	if !ok {
@@ -1077,19 +1013,9 @@
 		return fmt.Errorf("tls: server sent non-matching cipher suite %04x vs %04x", hs.suite.id, hs.serverHello.cipherSuite)
 	}
 
-	if haveHelloRetryRequest {
-		if helloRetryRequest.hasSelectedGroup && helloRetryRequest.selectedGroup != hs.serverHello.keyShare.group {
-			c.sendAlert(alertHandshakeFailure)
-			return errors.New("tls: ServerHello parameters did not match HelloRetryRequest")
-		}
-
-		// Both the ServerHello and HelloRetryRequest must have an ECH confirmation.
-		echConfirmed := hs.checkECHConfirmation(hs.serverHello, hs.hello, &hs.finishedHash)
-		if hs.hello.echInner && !echConfirmed {
-			return errors.New("tls: server did not send ECH confirmation in ServerHello when requested")
-		} else if !hs.hello.echInner && echConfirmed {
-			return errors.New("tls: server sent ECH confirmation in ServerHello when not requested")
-		}
+	if haveHelloRetryRequest && helloRetryRequest.hasSelectedGroup && helloRetryRequest.selectedGroup != hs.serverHello.keyShare.group {
+		c.sendAlert(alertHandshakeFailure)
+		return errors.New("tls: ServerHello parameters did not match HelloRetryRequest")
 	}
 
 	if !bytes.Equal(hs.hello.sessionID, hs.serverHello.sessionID) {
@@ -1113,6 +1039,9 @@
 		c.didResume = true
 	}
 	hs.finishedHash.addEntropy(pskSecret)
+	if hs.innerHello != nil {
+		hs.innerFinishedHash.addEntropy(pskSecret)
+	}
 
 	if !hs.serverHello.hasKeyShare {
 		c.sendAlert(alertUnsupportedExtension)
@@ -1137,6 +1066,39 @@
 	}
 	hs.finishedHash.nextSecret()
 	hs.finishedHash.addEntropy(ecdheSecret)
+	if hs.innerHello != nil {
+		hs.innerFinishedHash.nextSecret()
+		hs.innerFinishedHash.addEntropy(ecdheSecret)
+	}
+
+	// Determine whether the server accepted ECH.
+	confirmHash := &hs.finishedHash
+	if hs.innerHello != nil {
+		confirmHash = &hs.innerFinishedHash
+	}
+	echConfirmed := bytes.Equal(hs.serverHello.random[24:], confirmHash.deriveSecretPeek([]byte("ech accept confirmation"), hs.serverHello.marshalForECHConf())[:8])
+	if hs.innerHello != nil {
+		c.echAccepted = echConfirmed
+		if c.echAccepted {
+			hs.hello = hs.innerHello
+			hs.finishedHash = hs.innerFinishedHash
+		}
+		hs.innerHello = nil
+		hs.innerFinishedHash = finishedHash{}
+	} else {
+		// When not offering ECH, we may still expect a confirmation signal to
+		// test the backend server behavior.
+		if hs.hello.echIsInner {
+			if !echConfirmed {
+				return errors.New("tls: server did not send ECH confirmation when requested")
+			}
+		} else {
+			if echConfirmed {
+				return errors.New("tls: server did sent ECH confirmation when not requested")
+			}
+		}
+	}
+
 	hs.writeServerHash(hs.serverHello.marshal())
 
 	// Derive handshake traffic keys and switch read key to handshake
@@ -1503,53 +1465,58 @@
 }
 
 // applyHelloRetryRequest updates |hello| in-place based on |helloRetryRequest|.
-// If |outerHello| is not nil, |outerHello| will be updated to contain an
-// encrypted copy of |hello|.
-func (hs *clientHandshakeState) applyHelloRetryRequest(helloRetryRequest *helloRetryRequestMsg, hello, outerHello *clientHelloMsg) error {
+// If |innerHello| is not nil, this is the second ClientHelloOuter and should
+// contain an encrypted copy of |innerHello|
+func (hs *clientHandshakeState) applyHelloRetryRequest(helloRetryRequest *helloRetryRequestMsg, hello, innerHello *clientHelloMsg) error {
 	c := hs.c
 	firstHelloBytes := hello.marshal()
+	isInner := innerHello == nil && hs.echHPKEContext != nil
 	if len(helloRetryRequest.cookie) > 0 {
 		hello.tls13Cookie = helloRetryRequest.cookie
 	}
 
-	if c.config.Bugs.MisinterpretHelloRetryRequestCurve != 0 {
-		helloRetryRequest.hasSelectedGroup = true
-		helloRetryRequest.selectedGroup = c.config.Bugs.MisinterpretHelloRetryRequestCurve
-	}
-	if helloRetryRequest.hasSelectedGroup {
-		var hrrCurveFound bool
-		group := helloRetryRequest.selectedGroup
-		for _, curveID := range hello.supportedCurves {
-			if group == curveID {
-				hrrCurveFound = true
-				break
+	if innerHello != nil {
+		hello.keyShares = innerHello.keyShares
+	} else {
+		if c.config.Bugs.MisinterpretHelloRetryRequestCurve != 0 {
+			helloRetryRequest.hasSelectedGroup = true
+			helloRetryRequest.selectedGroup = c.config.Bugs.MisinterpretHelloRetryRequestCurve
+		}
+		if helloRetryRequest.hasSelectedGroup {
+			var hrrCurveFound bool
+			group := helloRetryRequest.selectedGroup
+			for _, curveID := range hello.supportedCurves {
+				if group == curveID {
+					hrrCurveFound = true
+					break
+				}
 			}
+			if !hrrCurveFound || hs.keyShares[group] != nil {
+				c.sendAlert(alertHandshakeFailure)
+				return errors.New("tls: received invalid HelloRetryRequest")
+			}
+			curve, ok := curveForCurveID(group, c.config)
+			if !ok {
+				return errors.New("tls: Unable to get curve requested in HelloRetryRequest")
+			}
+			publicKey, err := curve.offer(c.config.rand())
+			if err != nil {
+				return err
+			}
+			hs.keyShares[group] = curve
+			hello.keyShares = []keyShareEntry{{
+				group:       group,
+				keyExchange: publicKey,
+			}}
 		}
-		if !hrrCurveFound || hs.keyShares[group] != nil {
-			c.sendAlert(alertHandshakeFailure)
-			return errors.New("tls: received invalid HelloRetryRequest")
+
+		if c.config.Bugs.SecondClientHelloMissingKeyShare {
+			hello.hasKeyShares = false
 		}
-		curve, ok := curveForCurveID(group, c.config)
-		if !ok {
-			return errors.New("tls: Unable to get curve requested in HelloRetryRequest")
-		}
-		publicKey, err := curve.offer(c.config.rand())
-		if err != nil {
-			return err
-		}
-		hs.keyShares[group] = curve
-		hello.keyShares = []keyShareEntry{{
-			group:       group,
-			keyExchange: publicKey,
-		}}
 	}
 
-	if c.config.Bugs.SecondClientHelloMissingKeyShare {
-		hello.hasKeyShares = false
-	}
-
-	if c.config.Bugs.OmitSecondECHInner {
-		hello.echInner = false
+	if isInner && c.config.Bugs.OmitSecondECHIsInner {
+		hello.echIsInner = false
 	}
 
 	hello.hasEarlyData = c.config.Bugs.SendEarlyDataOnSecondClientHello
@@ -1557,51 +1524,56 @@
 	if c.config.Bugs.PSKBinderFirst && c.config.Bugs.OnlyCorruptSecondPSKBinder {
 		hello.prefixExtensions = append(hello.prefixExtensions, extensionPreSharedKey)
 	}
-	// The first ClientHello may have set this due to OnlyCompressSecondClientHelloInner.
-	hello.reorderOuterExtensionsWithoutCompressing = false
+	// The first ClientHello may have skipped this due to OnlyCompressSecondClientHelloInner.
+	if isInner && len(c.config.ECHOuterExtensions) > 0 && c.config.Bugs.OnlyCompressSecondClientHelloInner {
+		applyECHOuterExtensions(hello, c.config.ECHOuterExtensions)
+	}
 	if c.config.Bugs.OmitPSKsOnSecondClientHello {
 		hello.pskIdentities = nil
 		hello.pskBinders = nil
 	}
 	hello.raw = nil
 
-	if len(hello.pskIdentities) > 0 {
-		generatePSKBinders(c.wireVersion, hello, hs.session, firstHelloBytes, helloRetryRequest.marshal(), c.config)
-	}
-
-	if outerHello != nil {
-		outerHello.raw = nil
-		// We know the server has accepted ECH, so the ClientHelloOuter's fields
-		// are irrelevant. In the general case, the HelloRetryRequest may not
-		// even be valid for ClientHelloOuter. However, we copy the key shares
-		// from ClientHelloInner so they remain eligible for compression.
-		if !c.config.Bugs.MinimalClientHelloOuter {
-			outerHello.keyShares = hello.keyShares
-		}
-
+	if innerHello != nil {
 		if c.config.Bugs.OmitSecondEncryptedClientHello {
-			outerHello.echOuter = nil
+			hello.clientECH = nil
 		} else {
 			configID := c.config.ClientECHConfig.ConfigID
 			if c.config.Bugs.CorruptSecondEncryptedClientHelloConfigID {
 				configID ^= 1
 			}
-			if err := hs.encryptClientHello(outerHello, hello, configID, nil); err != nil {
+			if err := hs.encryptClientHello(hello, innerHello, configID, nil); err != nil {
 				return err
 			}
 			if c.config.Bugs.CorruptSecondEncryptedClientHello {
 				if c.config.Bugs.NullAllCiphers {
-					outerHello.echOuter.payload = []byte{echBadPayloadByte}
+					hello.clientECH.payload = []byte{echBadPayloadByte}
 				} else {
-					outerHello.echOuter.payload[0] ^= 1
+					hello.clientECH.payload[0] ^= 1
 				}
 			}
 		}
 	}
 
+	// PSK binders and ECH both must be inserted last because they incorporate
+	// the rest of the ClientHello and conflict. See corresponding comment in
+	// |createClientHello|.
+	if len(hello.pskIdentities) > 0 {
+		generatePSKBinders(c.wireVersion, hello, hs.session, firstHelloBytes, helloRetryRequest.marshal(), c.config)
+	}
 	return nil
 }
 
+// applyECHOuterExtensions updates |hello| to compress |outerExtensions| with
+// the ech_outer_extensions mechanism.
+func applyECHOuterExtensions(hello *clientHelloMsg, outerExtensions []uint16) {
+	// Ensure that the ech_outer_extensions extension and each of the
+	// extensions it names are serialized consecutively.
+	hello.prefixExtensions = append(hello.prefixExtensions, extensionECHOuterExtensions)
+	hello.prefixExtensions = append(hello.prefixExtensions, outerExtensions...)
+	hello.outerExtensions = outerExtensions
+}
+
 func (hs *clientHandshakeState) doFullHandshake() error {
 	c := hs.c
 
diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go
index f2ef2fc..d666a87 100644
--- a/src/ssl/test/runner/handshake_messages.go
+++ b/src/ssl/test/runner/handshake_messages.go
@@ -260,7 +260,7 @@
 	ConfigID     uint8
 	KEM          uint16
 	PublicKey    []byte
-	MaxNameLen   uint8
+	MaxNameLen   uint16
 	PublicName   string
 	CipherSuites []HPKECipherSuite
 	// The following fields are only used by CreateECHConfig().
@@ -282,8 +282,8 @@
 		cipherSuites.addU16(suite.KDF)
 		cipherSuites.addU16(suite.AEAD)
 	}
-	contents.addU8(template.MaxNameLen)
-	contents.addU8LengthPrefixed().addBytes([]byte(template.PublicName))
+	contents.addU16(template.MaxNameLen)
+	contents.addU16LengthPrefixed().addBytes([]byte(template.PublicName))
 	extensions := contents.addU16LengthPrefixed()
 	// Mandatory extensions have the high bit set.
 	if template.UnsupportedExtension {
@@ -318,12 +318,9 @@
 	Key       []byte
 }
 
-const (
-	echClientTypeOuter byte = 0
-	echClientTypeInner byte = 1
-)
-
-type echClientOuter struct {
+// The contents of a CH "encrypted_client_hello" extension.
+// https://tools.ietf.org/html/draft-ietf-tls-esni-10
+type clientECH struct {
 	kdfID    uint16
 	aeadID   uint16
 	configID uint8
@@ -332,64 +329,64 @@
 }
 
 type clientHelloMsg struct {
-	raw                                      []byte
-	isDTLS                                   bool
-	isV2ClientHello                          bool
-	vers                                     uint16
-	random                                   []byte
-	v2Challenge                              []byte
-	sessionID                                []byte
-	cookie                                   []byte
-	cipherSuites                             []uint16
-	compressionMethods                       []uint8
-	nextProtoNeg                             bool
-	serverName                               string
-	echOuter                                 *echClientOuter
-	echInner                                 bool
-	invalidECHInner                          []byte
-	ocspStapling                             bool
-	supportedCurves                          []CurveID
-	supportedPoints                          []uint8
-	hasKeyShares                             bool
-	keyShares                                []keyShareEntry
-	keySharesRaw                             []byte
-	trailingKeyShareData                     bool
-	pskIdentities                            []pskIdentity
-	pskKEModes                               []byte
-	pskBinders                               [][]uint8
-	hasEarlyData                             bool
-	tls13Cookie                              []byte
-	ticketSupported                          bool
-	sessionTicket                            []uint8
-	signatureAlgorithms                      []signatureAlgorithm
-	signatureAlgorithmsCert                  []signatureAlgorithm
-	supportedVersions                        []uint16
-	secureRenegotiation                      []byte
-	alpnProtocols                            []string
-	quicTransportParams                      []byte
-	quicTransportParamsLegacy                []byte
-	duplicateExtension                       bool
-	channelIDSupported                       bool
-	extendedMasterSecret                     bool
-	srtpProtectionProfiles                   []uint16
-	srtpMasterKeyIdentifier                  string
-	sctListSupported                         bool
-	customExtension                          string
-	hasGREASEExtension                       bool
-	omitExtensions                           bool
-	emptyExtensions                          bool
-	pad                                      int
-	compressedCertAlgs                       []uint16
-	delegatedCredentials                     bool
-	alpsProtocols                            []string
-	outerExtensions                          []uint16
-	reorderOuterExtensionsWithoutCompressing bool
-	prefixExtensions                         []uint16
+	raw                       []byte
+	isDTLS                    bool
+	isV2ClientHello           bool
+	vers                      uint16
+	random                    []byte
+	v2Challenge               []byte
+	sessionID                 []byte
+	cookie                    []byte
+	cipherSuites              []uint16
+	compressionMethods        []uint8
+	nextProtoNeg              bool
+	serverName                string
+	clientECH                 *clientECH
+	echIsInner                bool
+	invalidECHIsInner         []byte
+	ocspStapling              bool
+	supportedCurves           []CurveID
+	supportedPoints           []uint8
+	hasKeyShares              bool
+	keyShares                 []keyShareEntry
+	keySharesRaw              []byte
+	trailingKeyShareData      bool
+	pskIdentities             []pskIdentity
+	pskKEModes                []byte
+	pskBinders                [][]uint8
+	hasEarlyData              bool
+	tls13Cookie               []byte
+	ticketSupported           bool
+	sessionTicket             []uint8
+	signatureAlgorithms       []signatureAlgorithm
+	signatureAlgorithmsCert   []signatureAlgorithm
+	supportedVersions         []uint16
+	secureRenegotiation       []byte
+	alpnProtocols             []string
+	quicTransportParams       []byte
+	quicTransportParamsLegacy []byte
+	duplicateExtension        bool
+	channelIDSupported        bool
+	extendedMasterSecret      bool
+	srtpProtectionProfiles    []uint16
+	srtpMasterKeyIdentifier   string
+	sctListSupported          bool
+	customExtension           string
+	hasGREASEExtension        bool
+	omitExtensions            bool
+	emptyExtensions           bool
+	pad                       int
+	compressedCertAlgs        []uint16
+	delegatedCredentials      bool
+	alpsProtocols             []string
+	outerExtensions           []uint16
+	prefixExtensions          []uint16
 	// The following fields are only filled in by |unmarshal| and ignored when
 	// marshaling a new ClientHello.
-	echPayloadStart int
-	echPayloadEnd   int
-	rawExtensions   []byte
+	extensionStart    int
+	echExtensionStart int
+	echExtensionEnd   int
+	rawExtensions     map[uint16][]byte
 }
 
 func (m *clientHelloMsg) marshalKeyShares(bb *byteBuilder) {
@@ -408,6 +405,7 @@
 
 const (
 	clientHelloNormal clientHelloType = iota
+	clientHelloOuterAAD
 	clientHelloEncodedInner
 )
 
@@ -473,26 +471,35 @@
 			body: serverNameList.finish(),
 		})
 	}
-	if m.echOuter != nil {
+	if m.clientECH != nil && typ != clientHelloOuterAAD {
 		body := newByteBuilder()
-		body.addU8(echClientTypeOuter)
-		body.addU16(m.echOuter.kdfID)
-		body.addU16(m.echOuter.aeadID)
-		body.addU8(m.echOuter.configID)
-		body.addU16LengthPrefixed().addBytes(m.echOuter.enc)
-		body.addU16LengthPrefixed().addBytes(m.echOuter.payload)
+		body.addU16(m.clientECH.kdfID)
+		body.addU16(m.clientECH.aeadID)
+		body.addU8(m.clientECH.configID)
+		body.addU16LengthPrefixed().addBytes(m.clientECH.enc)
+		body.addU16LengthPrefixed().addBytes(m.clientECH.payload)
+
 		extensions = append(extensions, extension{
 			id:   extensionEncryptedClientHello,
 			body: body.finish(),
 		})
 	}
-	if m.echInner {
-		body := newByteBuilder()
-		body.addU8(echClientTypeInner)
-		// If unset, invalidECHInner is empty, which is the correct serialization.
-		body.addBytes(m.invalidECHInner)
+	if m.echIsInner {
 		extensions = append(extensions, extension{
-			id:   extensionEncryptedClientHello,
+			id: extensionECHIsInner,
+			// If unset, invalidECHIsInner is empty, which is the correct
+			// serialization.
+			body: m.invalidECHIsInner,
+		})
+	}
+	if m.outerExtensions != nil && typ == clientHelloEncodedInner {
+		body := newByteBuilder()
+		extensionsList := body.addU8LengthPrefixed()
+		for _, extID := range m.outerExtensions {
+			extensionsList.addU16(extID)
+		}
+		extensions = append(extensions, extension{
+			id:   extensionECHOuterExtensions,
 			body: body.finish(),
 		})
 	}
@@ -661,7 +668,7 @@
 	if m.sctListSupported {
 		extensions = append(extensions, extension{id: extensionSignedCertificateTimestamp})
 	}
-	if len(m.customExtension) > 0 {
+	if l := len(m.customExtension); l > 0 {
 		extensions = append(extensions, extension{
 			id:   extensionCustom,
 			body: []byte(m.customExtension),
@@ -719,51 +726,35 @@
 		})
 	}
 
+	// Write each extension in |extensions| to the |hello| message, hoisting
+	// the extensions named in |m.prefixExtensions| to the front.
 	extensionsBB := hello.addU16LengthPrefixed()
 	extMap := make(map[uint16][]byte)
-	extsWritten := make(map[uint16]struct{})
 	for _, ext := range extensions {
 		extMap[ext.id] = ext.body
 	}
+	// Elide each of the extensions named by |m.outerExtensions|.
+	if m.outerExtensions != nil && typ == clientHelloEncodedInner {
+		for _, extID := range m.outerExtensions {
+			delete(extMap, extID)
+		}
+	}
 	// Write each of the prefix extensions, if we have it.
 	for _, extID := range m.prefixExtensions {
 		if body, ok := extMap[extID]; ok {
 			extensionsBB.addU16(extID)
 			extensionsBB.addU16LengthPrefixed().addBytes(body)
-			extsWritten[extID] = struct{}{}
 		}
 	}
-	// Write outer extensions, possibly in compressed form.
-	if m.outerExtensions != nil {
-		if typ == clientHelloEncodedInner && !m.reorderOuterExtensionsWithoutCompressing {
-			extensionsBB.addU16(extensionECHOuterExtensions)
-			list := extensionsBB.addU16LengthPrefixed().addU8LengthPrefixed()
-			for _, extID := range m.outerExtensions {
-				list.addU16(extID)
-				extsWritten[extID] = struct{}{}
-			}
-		} else {
-			for _, extID := range m.outerExtensions {
-				// m.outerExtensions may intentionally contain duplicates to test the
-				// server's reaction. If m.reorderOuterExtensionsWithoutCompressing
-				// is set, we are targetting the second ClientHello and wish to send a
-				// valid first ClientHello. In that case, deduplicate so the error
-				// only appears later.
-				if _, written := extsWritten[extID]; m.reorderOuterExtensionsWithoutCompressing && written {
-					continue
-				}
-				if body, ok := extMap[extID]; ok {
-					extensionsBB.addU16(extID)
-					extensionsBB.addU16LengthPrefixed().addBytes(body)
-					extsWritten[extID] = struct{}{}
-				}
-			}
-		}
+	// Forget each of the prefix extensions. This loop is separate from the
+	// extension-writing loop because |m.prefixExtensions| may contain
+	// duplicates.
+	for _, extID := range m.prefixExtensions {
+		delete(extMap, extID)
 	}
-
 	// Write each of the remaining extensions in their original order.
 	for _, ext := range extensions {
-		if _, written := extsWritten[ext.id]; !written {
+		if _, ok := extMap[ext.id]; ok {
 			extensionsBB.addU16(ext.id)
 			extensionsBB.addU16LengthPrefixed().addBytes(ext.body)
 		}
@@ -788,6 +779,10 @@
 	}
 }
 
+func (m *clientHelloMsg) marshalForOuterAAD(bb *byteBuilder) {
+	m.marshalBody(bb, clientHelloOuterAAD)
+}
+
 func (m *clientHelloMsg) marshalForEncodedInner() []byte {
 	hello := newByteBuilder()
 	m.marshalBody(hello, clientHelloEncodedInner)
@@ -896,6 +891,8 @@
 		}
 	}
 
+	m.extensionStart = len(data) - len(reader)
+
 	m.nextProtoNeg = false
 	m.serverName = ""
 	m.ocspStapling = false
@@ -912,6 +909,7 @@
 	m.customExtension = ""
 	m.delegatedCredentials = false
 	m.alpsProtocols = nil
+	m.rawExtensions = make(map[uint16][]byte)
 
 	if len(reader) == 0 {
 		// ClientHello is optionally followed by extension data
@@ -922,7 +920,6 @@
 	if !reader.readU16LengthPrefixed(&extensions) || len(reader) != 0 || !checkDuplicateExtensions(extensions) {
 		return false
 	}
-	m.rawExtensions = extensions
 	for len(extensions) > 0 {
 		var extension uint16
 		var body byteReader
@@ -930,6 +927,7 @@
 			!extensions.readU16LengthPrefixed(&body) {
 			return false
 		}
+		m.rawExtensions[extension] = body
 		switch extension {
 		case extensionServerName:
 			var names byteReader
@@ -948,33 +946,24 @@
 				}
 			}
 		case extensionEncryptedClientHello:
-			var typ byte
-			if !body.readU8(&typ) {
+			m.echExtensionEnd = len(data) - len(extensions)
+			m.echExtensionStart = m.echExtensionEnd - len(body) - 4
+			var ech clientECH
+			if !body.readU16(&ech.kdfID) ||
+				!body.readU16(&ech.aeadID) ||
+				!body.readU8(&ech.configID) ||
+				!body.readU16LengthPrefixedBytes(&ech.enc) ||
+				!body.readU16LengthPrefixedBytes(&ech.payload) ||
+				len(ech.payload) == 0 ||
+				len(body) > 0 {
 				return false
 			}
-			switch typ {
-			case echClientTypeOuter:
-				var echOuter echClientOuter
-				if !body.readU16(&echOuter.kdfID) ||
-					!body.readU16(&echOuter.aeadID) ||
-					!body.readU8(&echOuter.configID) ||
-					!body.readU16LengthPrefixedBytes(&echOuter.enc) ||
-					!body.readU16LengthPrefixedBytes(&echOuter.payload) ||
-					len(echOuter.payload) == 0 ||
-					len(body) > 0 {
-					return false
-				}
-				m.echOuter = &echOuter
-				m.echPayloadEnd = len(data) - len(extensions)
-				m.echPayloadStart = m.echPayloadEnd - len(echOuter.payload)
-			case echClientTypeInner:
-				if len(body) > 0 {
-					return false
-				}
-				m.echInner = true
-			default:
+			m.clientECH = &ech
+		case extensionECHIsInner:
+			if len(body) != 0 {
 				return false
 			}
+			m.echIsInner = true
 		case extensionNextProtoNeg:
 			if len(body) != 0 {
 				return false
@@ -1206,6 +1195,13 @@
 		}
 	}
 
+	// Clients may not send both extensions.
+	// TODO(davidben): A later draft will likely merge the code points, at which
+	// point this check will be redundant.
+	if m.echIsInner && m.clientECH != nil {
+		return false
+	}
+
 	return true
 }
 
@@ -1218,17 +1214,11 @@
 		len(sessionID) != 0 || // Copied from |helloOuter|
 		!reader.readU16LengthPrefixedBytes(&cipherSuites) ||
 		!reader.readU8LengthPrefixedBytes(&compressionMethods) ||
-		!reader.readU16LengthPrefixed(&extensions) {
+		!reader.readU16LengthPrefixed(&extensions) ||
+		len(reader) != 0 {
 		return nil, errors.New("tls: error parsing EncodedClientHelloInner")
 	}
 
-	// The remainder of the structure is padding.
-	for _, padding := range reader {
-		if padding != 0 {
-			return nil, errors.New("tls: non-zero padding in EncodedClientHelloInner")
-		}
-	}
-
 	builder := newByteBuilder()
 	builder.addU8(typeClientHello)
 	body := builder.addU24LengthPrefixed()
@@ -1239,7 +1229,6 @@
 	newExtensions := body.addU16LengthPrefixed()
 
 	var seenOuterExtensions bool
-	outerExtensions := byteReader(helloOuter.rawExtensions)
 	copied := make(map[uint16]struct{})
 	for len(extensions) > 0 {
 		var extType uint16
@@ -1257,35 +1246,28 @@
 			return nil, errors.New("tls: duplicate ech_outer_extensions extension")
 		}
 		seenOuterExtensions = true
-		var extList byteReader
-		if !extBody.readU8LengthPrefixed(&extList) || len(extList) == 0 || len(extBody) != 0 {
+		var outerExtensions byteReader
+		if !extBody.readU8LengthPrefixed(&outerExtensions) || len(outerExtensions) == 0 || len(extBody) != 0 {
 			return nil, errors.New("tls: error parsing ech_outer_extensions")
 		}
-		for len(extList) != 0 {
+		for len(outerExtensions) != 0 {
 			var newExtType uint16
-			if !extList.readU16(&newExtType) {
+			if !outerExtensions.readU16(&newExtType) {
 				return nil, errors.New("tls: error parsing ech_outer_extensions")
 			}
 			if newExtType == extensionEncryptedClientHello {
 				return nil, errors.New("tls: error parsing ech_outer_extensions")
 			}
-			for {
-				if len(outerExtensions) == 0 {
-					return nil, fmt.Errorf("tls: extension %d not found in ClientHelloOuter", newExtType)
-				}
-				var foundExt uint16
-				var newExtBody []byte
-				if !outerExtensions.readU16(&foundExt) ||
-					!outerExtensions.readU16LengthPrefixedBytes(&newExtBody) {
-					return nil, errors.New("tls: error parsing ClientHelloOuter")
-				}
-				if foundExt == newExtType {
-					newExtensions.addU16(newExtType)
-					newExtensions.addU16LengthPrefixed().addBytes(newExtBody)
-					copied[newExtType] = struct{}{}
-					break
-				}
+			if _, ok := copied[newExtType]; ok {
+				return nil, errors.New("tls: duplicate extension in ech_outer_extensions")
 			}
+			newExtBody, ok := helloOuter.rawExtensions[newExtType]
+			if !ok {
+				return nil, fmt.Errorf("tls: extension %d not found in ClientHelloOuter", newExtType)
+			}
+			newExtensions.addU16(newExtType)
+			newExtensions.addU16LengthPrefixed().addBytes(newExtBody)
+			copied[newExtType] = struct{}{}
 		}
 	}
 
@@ -1505,6 +1487,25 @@
 	return true
 }
 
+// marshalForECHConf marshals |m|, but zeroes out the last 8 bytes of the
+// ServerHello.random.
+func (m *serverHelloMsg) marshalForECHConf() []byte {
+	ret := m.marshal()
+	// Make a copy so we can mutate it.
+	ret = append(make([]byte, 0, len(ret)), ret...)
+
+	reparsed := new(serverHelloMsg)
+	if !reparsed.unmarshal(ret) {
+		panic("could not re-parse ServerHello")
+	}
+	// We rely on |unmarshal| aliasing the |random| into |ret|.
+	for i := 24; i < 32; i++ {
+		reparsed.random[i] = 0
+	}
+
+	return ret
+}
+
 type encryptedExtensionsMsg struct {
 	raw        []byte
 	extensions serverExtensions
@@ -1906,18 +1907,16 @@
 }
 
 type helloRetryRequestMsg struct {
-	raw                   []byte
-	vers                  uint16
-	sessionID             []byte
-	cipherSuite           uint16
-	compressionMethod     uint8
-	hasSelectedGroup      bool
-	selectedGroup         CurveID
-	cookie                []byte
-	customExtension       string
-	echConfirmation       []byte
-	echConfirmationOffset int
-	duplicateExtensions   bool
+	raw                 []byte
+	vers                uint16
+	sessionID           []byte
+	cipherSuite         uint16
+	compressionMethod   uint8
+	hasSelectedGroup    bool
+	selectedGroup       CurveID
+	cookie              []byte
+	customExtension     string
+	duplicateExtensions bool
 }
 
 func (m *helloRetryRequestMsg) marshal() []byte {
@@ -1961,10 +1960,6 @@
 			extensions.addU16(extensionCustom)
 			extensions.addU16LengthPrefixed().addBytes([]byte(m.customExtension))
 		}
-		if len(m.echConfirmation) > 0 {
-			extensions.addU16(extensionEncryptedClientHello)
-			extensions.addU16LengthPrefixed().addBytes(m.echConfirmation)
-		}
 	}
 
 	m.raw = retryRequestMsg.finish()
@@ -2010,17 +2005,9 @@
 			m.hasSelectedGroup = true
 			m.selectedGroup = CurveID(v)
 		case extensionCookie:
-			if !body.readU16LengthPrefixedBytes(&m.cookie) ||
-				len(m.cookie) == 0 ||
-				len(body) != 0 {
+			if !body.readU16LengthPrefixedBytes(&m.cookie) || len(body) != 0 {
 				return false
 			}
-		case extensionEncryptedClientHello:
-			if len(body) != echAcceptConfirmationLength {
-				return false
-			}
-			m.echConfirmation = body
-			m.echConfirmationOffset = len(m.raw) - len(extensions) - len(body)
 		default:
 			// Unknown extensions are illegal from the server.
 			return false
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 4f41184..b9d7667 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -186,21 +186,14 @@
 		return errors.New("tls: no GREASE extension found")
 	}
 
-	if config.Bugs.ExpectClientECH && hs.clientHello.echOuter == nil {
-		return errors.New("tls: expected client to offer ECH")
-	}
-	if config.Bugs.ExpectNoClientECH && hs.clientHello.echOuter != nil {
-		return errors.New("tls: expected client not to offer ECH")
-	}
-
-	if echOuter := hs.clientHello.echOuter; echOuter != nil {
+	if clientECH := hs.clientHello.clientECH; clientECH != nil {
 		for _, candidate := range config.ServerECHConfigs {
-			if candidate.ECHConfig.ConfigID != echOuter.configID {
+			if candidate.ECHConfig.ConfigID != clientECH.configID {
 				continue
 			}
 			var found bool
 			for _, suite := range candidate.ECHConfig.CipherSuites {
-				if echOuter.kdfID == suite.KDF && echOuter.aeadID == suite.AEAD {
+				if clientECH.kdfID == suite.KDF && clientECH.aeadID == suite.AEAD {
 					found = true
 					break
 				}
@@ -210,7 +203,7 @@
 			}
 			info := []byte("tls ech\x00")
 			info = append(info, candidate.ECHConfig.Raw...)
-			hs.echHPKEContext, err = hpke.SetupBaseReceiverX25519(echOuter.kdfID, echOuter.aeadID, echOuter.enc, candidate.Key, info)
+			hs.echHPKEContext, err = hpke.SetupBaseReceiverX25519(clientECH.kdfID, clientECH.aeadID, clientECH.enc, candidate.Key, info)
 			if err != nil {
 				continue
 			}
@@ -227,7 +220,7 @@
 			} else {
 				c.echAccepted = true
 				hs.clientHello = clientHelloInner
-				hs.echConfigID = echOuter.configID
+				hs.echConfigID = clientECH.configID
 			}
 		}
 	}
@@ -456,17 +449,29 @@
 }
 
 func (hs *serverHandshakeState) decryptClientHello(helloOuter *clientHelloMsg) (helloInner *clientHelloMsg, err error) {
-	// ClientHelloOuterAAD is ClientHelloOuter with the payload replaced by
-	// zeros. See draft-ietf-tls-esni-13, section 5.2.
-	aad := make([]byte, len(helloOuter.raw)-4)
-	copy(aad, helloOuter.raw[4:helloOuter.echPayloadStart])
-	copy(aad[helloOuter.echPayloadEnd-4:], helloOuter.raw[helloOuter.echPayloadEnd:])
+	// See draft-ietf-tls-esni-10, section 5.2.
+	aad := newByteBuilder()
+	aad.addU16(helloOuter.clientECH.kdfID)
+	aad.addU16(helloOuter.clientECH.aeadID)
+	aad.addU8(helloOuter.clientECH.configID)
+	aad.addU16LengthPrefixed().addBytes(helloOuter.clientECH.enc)
+	// ClientHelloOuterAAD.outer_hello is ClientHelloOuter without the
+	// encrypted_client_hello extension. Construct this by piecing together
+	// the preserved portions from offsets and updating the length prefix.
+	//
+	// TODO(davidben): If https://github.com/tlswg/draft-ietf-tls-esni/pull/442
+	// is merged, a later iteration will hopefully be simpler.
+	outerHello := aad.addU24LengthPrefixed()
+	outerHello.addBytes(helloOuter.raw[4:helloOuter.extensionStart])
+	extensions := outerHello.addU16LengthPrefixed()
+	extensions.addBytes(helloOuter.raw[helloOuter.extensionStart+2 : helloOuter.echExtensionStart])
+	extensions.addBytes(helloOuter.raw[helloOuter.echExtensionEnd:])
 
 	// In fuzzer mode, the payload is cleartext.
-	encoded := helloOuter.echOuter.payload
+	encoded := helloOuter.clientECH.payload
 	if !hs.c.config.Bugs.NullAllCiphers {
 		var err error
-		encoded, err = hs.echHPKEContext.Open(helloOuter.echOuter.payload, aad)
+		encoded, err = hs.echHPKEContext.Open(helloOuter.clientECH.payload, aad.finish())
 		if err != nil {
 			// Wrap |err| so the caller can implement trial decryption.
 			return nil, &echDecryptError{err}
@@ -500,8 +505,8 @@
 	if helloInner.nextProtoNeg || len(helloInner.supportedPoints) != 0 || helloInner.ticketSupported || helloInner.secureRenegotiation != nil || helloInner.extendedMasterSecret {
 		return nil, errors.New("tls: ClientHelloInner included a TLS-1.2-only extension")
 	}
-	if !helloInner.echInner {
-		return nil, errors.New("tls: ClientHelloInner missing inner encrypted_client_hello extension")
+	if !helloInner.echIsInner {
+		return nil, errors.New("tls: ClientHelloInner missing ech_is_inner extension")
 	}
 
 	return helloInner, nil
@@ -544,6 +549,13 @@
 		return err
 	}
 
+	if config.Bugs.ExpectClientECH && hs.clientHello.clientECH == nil {
+		return errors.New("tls: expected client to offer ECH")
+	}
+	if config.Bugs.ExpectNoClientECH && hs.clientHello.clientECH != nil {
+		return errors.New("tls: expected client not to offer ECH")
+	}
+
 	// Select the cipher suite.
 	var preferenceList, supportedList []uint16
 	if config.PreferServerCipherSuites {
@@ -742,21 +754,6 @@
 
 	if sendHelloRetryRequest {
 		hs.finishedHash.UpdateForHelloRetryRequest()
-
-		// Emit the ECH confirmation signal when requested.
-		if hs.clientHello.echInner {
-			helloRetryRequest.echConfirmation = make([]byte, 8)
-			helloRetryRequest.echConfirmation = hs.finishedHash.echAcceptConfirmation(hs.clientHello.random, echAcceptConfirmationHRRLabel, helloRetryRequest.marshal())
-			helloRetryRequest.raw = nil
-		} else if config.Bugs.AlwaysSendECHHelloRetryRequest {
-			// When solicited, a random ECH confirmation string should be ignored.
-			helloRetryRequest.echConfirmation = make([]byte, 8)
-			if _, err := io.ReadFull(config.rand(), helloRetryRequest.echConfirmation); err != nil {
-				c.sendAlert(alertInternalError)
-				return fmt.Errorf("tls: short read from Rand: %s", err)
-			}
-		}
-
 		hs.writeServerHash(helloRetryRequest.marshal())
 		if c.config.Bugs.PartialServerHelloWithHelloRetryRequest {
 			data := helloRetryRequest.marshal()
@@ -790,19 +787,19 @@
 		}
 
 		if c.echAccepted {
-			if newClientHello.echOuter == nil {
+			if newClientHello.clientECH == nil {
 				c.sendAlert(alertMissingExtension)
 				return errors.New("tls: second ClientHelloOuter had no encrypted_client_hello extension")
 			}
-			if newClientHello.echOuter.configID != hs.echConfigID ||
-				newClientHello.echOuter.kdfID != hs.echHPKEContext.KDF() ||
-				newClientHello.echOuter.aeadID != hs.echHPKEContext.AEAD() {
+			if newClientHello.clientECH.configID != hs.echConfigID ||
+				newClientHello.clientECH.kdfID != hs.echHPKEContext.KDF() ||
+				newClientHello.clientECH.aeadID != hs.echHPKEContext.AEAD() {
 				c.sendAlert(alertIllegalParameter)
 				return errors.New("tls: ECH parameters changed in second ClientHelloOuter")
 			}
-			if len(newClientHello.echOuter.enc) != 0 {
+			if len(newClientHello.clientECH.enc) != 0 {
 				c.sendAlert(alertIllegalParameter)
-				return errors.New("tls: second ClientHelloOuter had non-empty ECH enc")
+				return errors.New("tls: second ClientECH had non-empty enc")
 			}
 			newClientHello, err = hs.decryptClientHello(newClientHello)
 			if err != nil {
@@ -822,6 +819,11 @@
 		// Check that the new ClientHello matches the old ClientHello,
 		// except for relevant modifications. See RFC 8446, section 4.1.2.
 		ignoreExtensions := []uint16{extensionPadding}
+		// TODO(https://crbug.com/boringssl/275): draft-ietf-tls-esni-10 requires
+		// violating the RFC 8446 rules. See
+		// https://github.com/tlswg/draft-ietf-tls-esni/issues/358
+		// A later draft will likely fix this. Remove this line if it does.
+		ignoreExtensions = append(ignoreExtensions, extensionEncryptedClientHello)
 
 		if helloRetryRequest.hasSelectedGroup {
 			newKeyShares := newClientHello.keyShares
@@ -1004,13 +1006,13 @@
 		hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret())
 	}
 
-	// Emit the ECH confirmation signal when requested.
-	if hs.clientHello.echInner && !config.Bugs.OmitServerHelloECHConfirmation {
-		randomSuffix := hs.hello.random[len(hs.hello.random)-echAcceptConfirmationLength:]
-		for i := range randomSuffix {
-			randomSuffix[i] = 0
+	// Overwrite part of ServerHello.random to signal ECH acceptance to the client.
+	if hs.clientHello.echIsInner {
+		for i := 24; i < 32; i++ {
+			hs.hello.random[i] = 0
 		}
-		copy(randomSuffix, hs.finishedHash.echAcceptConfirmation(hs.clientHello.random, echAcceptConfirmationLabel, hs.hello.marshal()))
+		echAcceptConfirmation := hs.finishedHash.deriveSecretPeek([]byte("ech accept confirmation"), hs.hello.marshal())
+		copy(hs.hello.random[24:], echAcceptConfirmation)
 		hs.hello.raw = nil
 	}
 
@@ -1694,7 +1696,7 @@
 
 	serverExtensions.serverNameAck = c.config.Bugs.SendServerNameAck
 
-	if (c.vers >= VersionTLS13 && hs.clientHello.echOuter != nil) || c.config.Bugs.AlwaysSendECHRetryConfigs {
+	if (c.vers >= VersionTLS13 || c.config.Bugs.SendECHRetryConfigsInTLS12ServerHello) && hs.clientHello.clientECH != nil {
 		if len(config.Bugs.SendECHRetryConfigs) > 0 {
 			serverExtensions.echRetryConfigs = config.Bugs.SendECHRetryConfigs
 		} else if len(config.ServerECHConfigs) > 0 {
@@ -1847,11 +1849,7 @@
 
 	// Generate a session ID if we're to save the session.
 	if !hs.hello.extensions.ticketSupported && config.ServerSessionCache != nil {
-		l := config.Bugs.NewSessionIDLength
-		if l == 0 {
-			l = 32
-		}
-		hs.hello.sessionID = make([]byte, l)
+		hs.hello.sessionID = make([]byte, 32)
 		if _, err := io.ReadFull(config.rand(), hs.hello.sessionID); err != nil {
 			c.sendAlert(alertInternalError)
 			return errors.New("tls: short read from Rand: " + err.Error())
diff --git a/src/ssl/test/runner/hpke/hpke.go b/src/ssl/test/runner/hpke/hpke.go
index 36dc637..a08538b 100644
--- a/src/ssl/test/runner/hpke/hpke.go
+++ b/src/ssl/test/runner/hpke/hpke.go
@@ -136,8 +136,6 @@
 
 func (c *Context) AEAD() uint16 { return c.aeadID }
 
-func (c *Context) Overhead() int { return c.aead.Overhead() }
-
 func (c *Context) Seal(plaintext, additionalData []byte) []byte {
 	ciphertext := c.aead.Seal(nil, c.computeNonce(), plaintext, additionalData)
 	c.incrementSeq()
diff --git a/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go
index 5731be0..66c427f 100644
--- a/src/ssl/test/runner/prf.go
+++ b/src/ssl/test/runner/prf.go
@@ -381,6 +381,7 @@
 	return b
 }
 
+// The following are labels for traffic secret derivation in TLS 1.3.
 var (
 	externalPSKBinderLabel        = []byte("ext binder")
 	resumptionPSKBinderLabel      = []byte("res binder")
@@ -395,25 +396,21 @@
 	resumptionLabel               = []byte("res master")
 
 	resumptionPSKLabel = []byte("resumption")
-
-	echAcceptConfirmationLabel    = []byte("ech accept confirmation")
-	echAcceptConfirmationHRRLabel = []byte("hrr ech accept confirmation")
 )
 
 // deriveSecret implements TLS 1.3's Derive-Secret function, as defined in
-// section 7.1 of RFC8446.
+// section 7.1 of draft ietf-tls-tls13-16.
 func (h *finishedHash) deriveSecret(label []byte) []byte {
 	return hkdfExpandLabel(h.suite.hash(), h.secret, label, h.appendContextHashes(nil), h.hash.Size())
 }
 
-// echConfirmation computes the ECH accept confirmation signal, as defined in
-// sections 7.2 and 7.2.1 of draft-ietf-tls-esni-13. The transcript hash is
-// computed by concatenating |h| with |extraMessages|.
-func (h *finishedHash) echAcceptConfirmation(clientRandom, label, extraMessages []byte) []byte {
-	secret := hkdf.Extract(h.suite.hash().New, clientRandom, h.zeroSecret())
-	hashCopy := copyHash(h.hash, h.suite.hash())
-	hashCopy.Write(extraMessages)
-	return hkdfExpandLabel(h.suite.hash(), secret, label, hashCopy.Sum(nil), echAcceptConfirmationLength)
+// deriveSecretPeek is the same as deriveSecret, but it enables the caller to
+// tentatively append messages to the transcript. The |extraMessages| parameter
+// contains the bytes of these tentative messages.
+func (h *finishedHash) deriveSecretPeek(label []byte, extraMessages []byte) []byte {
+	hashPeek := copyHash(h.hash, h.suite.hash())
+	hashPeek.Write(extraMessages)
+	return hkdfExpandLabel(h.suite.hash(), h.secret, label, hashPeek.Sum(nil), h.hash.Size())
 }
 
 // The following are context strings for CertificateVerify in TLS 1.3.
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index cfff714..3306c88 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -1636,7 +1636,6 @@
 	if err != nil {
 		return err
 	}
-	statusChan <- statusMsg{test: test, statusType: statusShimStarted, pid: shim.cmd.Process.Pid}
 	defer shim.close()
 
 	localErr := doExchanges(test, shim, resumeCount, &transcripts)
@@ -3833,7 +3832,7 @@
 			},
 		},
 		shouldFail:    true,
-		expectedError: ":WRONG_CIPHER_RETURNED:",
+		expectedError: ":UNKNOWN_CIPHER_RETURNED:",
 	})
 	testCases = append(testCases, testCase{
 		name: "ServerHelloBogusCipher-TLS13",
@@ -13278,7 +13277,7 @@
 
 	testCases = append(testCases, testCase{
 		testType: serverTest,
-		name:     "Server-ShortSessionID-TLS13",
+		name:     "ShortSessionID-TLS13",
 		config: Config{
 			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
@@ -13289,7 +13288,7 @@
 
 	testCases = append(testCases, testCase{
 		testType: serverTest,
-		name:     "Server-FullSessionID-TLS13",
+		name:     "FullSessionID-TLS13",
 		config: Config{
 			MaxVersion: VersionTLS13,
 			Bugs: ProtocolBugs{
@@ -13298,62 +13297,6 @@
 		},
 	})
 
-	// The server should reject ClientHellos whose session IDs are too long.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "Server-TooLongSessionID-TLS13",
-		config: Config{
-			MaxVersion: VersionTLS13,
-			Bugs: ProtocolBugs{
-				SendClientHelloSessionID: make([]byte, 33),
-			},
-		},
-		shouldFail:         true,
-		expectedError:      ":DECODE_ERROR:",
-		expectedLocalError: "remote error: error decoding message",
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "Server-TooLongSessionID-TLS12",
-		config: Config{
-			MaxVersion: VersionTLS12,
-			Bugs: ProtocolBugs{
-				SendClientHelloSessionID: make([]byte, 33),
-			},
-		},
-		shouldFail:         true,
-		expectedError:      ":DECODE_ERROR:",
-		expectedLocalError: "remote error: error decoding message",
-	})
-
-	// Test that the client correctly accepts or rejects short session IDs from
-	// the server. Our tests use 32 bytes by default, so the boundary condition
-	// is already covered.
-	testCases = append(testCases, testCase{
-		name: "Client-ShortSessionID",
-		config: Config{
-			MaxVersion:             VersionTLS12,
-			SessionTicketsDisabled: true,
-			Bugs: ProtocolBugs{
-				NewSessionIDLength: 1,
-			},
-		},
-		resumeSession: true,
-	})
-	testCases = append(testCases, testCase{
-		name: "Client-TooLongSessionID",
-		config: Config{
-			MaxVersion:             VersionTLS12,
-			SessionTicketsDisabled: true,
-			Bugs: ProtocolBugs{
-				NewSessionIDLength: 33,
-			},
-		},
-		shouldFail:         true,
-		expectedError:      ":DECODE_ERROR:",
-		expectedLocalError: "remote error: error decoding message",
-	})
-
 	// Test that the client sends a fake session ID in TLS 1.3. We cover both
 	// normal and resumption handshakes to capture interactions with the
 	// session resumption path.
@@ -13884,7 +13827,7 @@
 			},
 		},
 		shouldFail:    true,
-		expectedError: ":DECODE_ERROR:",
+		expectedError: ":WRONG_VERSION_NUMBER:",
 	})
 
 	testCases = append(testCases, testCase{
@@ -16645,19 +16588,19 @@
 				expectedError:      ":INVALID_CLIENT_HELLO_INNER:",
 			})
 
-			// When inner ECH extension is absent from the ClientHelloInner, the
+			// When ech_is_inner extension is absent from the ClientHelloInner, the
 			// server should fail the connection.
 			testCases = append(testCases, testCase{
 				testType: serverTest,
 				protocol: protocol,
-				name:     prefix + "ECH-Server-MissingECHInner" + suffix,
+				name:     prefix + "ECH-Server-MissingECHIsInner" + suffix,
 				config: Config{
 					ServerName:      "secret.example",
 					DefaultCurves:   defaultCurves,
 					ClientECHConfig: echConfig.ECHConfig,
 					Bugs: ProtocolBugs{
-						OmitECHInner:       !hrr,
-						OmitSecondECHInner: hrr,
+						OmitECHIsInner:       !hrr,
+						OmitSecondECHIsInner: hrr,
 					},
 				},
 				flags: []string{
@@ -16687,7 +16630,11 @@
 						extensionCustom,
 					},
 					Bugs: ProtocolBugs{
-						CustomExtension:                    "test",
+						CustomExtension: "test",
+						// Ensure ClientHelloOuter's extension order is different
+						// from ClientHelloInner. This tests that the server
+						// correctly reconstructs the extension order.
+						FirstExtensionInClientHelloOuter:   extensionSupportedCurves,
 						OnlyCompressSecondClientHelloInner: hrr,
 					},
 				},
@@ -16703,84 +16650,6 @@
 				},
 			})
 
-			// Test that the server allows referenced ClientHelloOuter
-			// extensions to be interleaved with other extensions. Only the
-			// relative order must match.
-			testCases = append(testCases, testCase{
-				testType: serverTest,
-				protocol: protocol,
-				name:     prefix + "ECH-Server-OuterExtensions-Interleaved" + suffix,
-				config: Config{
-					ServerName:      "secret.example",
-					DefaultCurves:   defaultCurves,
-					ClientECHConfig: echConfig.ECHConfig,
-					ECHOuterExtensions: []uint16{
-						extensionKeyShare,
-						extensionSupportedCurves,
-						extensionCustom,
-					},
-					Bugs: ProtocolBugs{
-						CustomExtension:                    "test",
-						OnlyCompressSecondClientHelloInner: hrr,
-						ECHOuterExtensionOrder: []uint16{
-							extensionServerName,
-							extensionKeyShare,
-							extensionSupportedVersions,
-							extensionPSKKeyExchangeModes,
-							extensionSupportedCurves,
-							extensionSignatureAlgorithms,
-							extensionCustom,
-						},
-					},
-				},
-				flags: []string{
-					"-ech-server-config", base64FlagValue(echConfig.ECHConfig.Raw),
-					"-ech-server-key", base64FlagValue(echConfig.Key),
-					"-ech-is-retry-config", "1",
-					"-expect-server-name", "secret.example",
-					"-expect-ech-accept",
-				},
-				expectations: connectionExpectations{
-					echAccepted: true,
-				},
-			})
-
-			// Test that the server rejects references to extensions in the
-			// wrong order.
-			testCases = append(testCases, testCase{
-				testType: serverTest,
-				protocol: protocol,
-				name:     prefix + "ECH-Server-OuterExtensions-WrongOrder" + suffix,
-				config: Config{
-					ServerName:      "secret.example",
-					DefaultCurves:   defaultCurves,
-					ClientECHConfig: echConfig.ECHConfig,
-					ECHOuterExtensions: []uint16{
-						extensionKeyShare,
-						extensionSupportedCurves,
-					},
-					Bugs: ProtocolBugs{
-						CustomExtension:                    "test",
-						OnlyCompressSecondClientHelloInner: hrr,
-						ECHOuterExtensionOrder: []uint16{
-							extensionSupportedCurves,
-							extensionKeyShare,
-						},
-					},
-				},
-				flags: []string{
-					"-ech-server-config", base64FlagValue(echConfig.ECHConfig.Raw),
-					"-ech-server-key", base64FlagValue(echConfig.Key),
-					"-ech-is-retry-config", "1",
-					"-expect-server-name", "secret.example",
-				},
-				shouldFail:         true,
-				expectedLocalError: "remote error: illegal parameter",
-				// The decoding algorithm relies on the ordering requirement, so
-				// the wrong order appears as a missing extension.
-				expectedError: ":OUTER_EXTENSION_NOT_FOUND:",
-			})
-
 			// Test that the server rejects duplicated values in ech_outer_extensions.
 			// Besides causing the server to reconstruct an invalid ClientHelloInner
 			// with duplicated extensions, this behavior would be vulnerable to DoS
@@ -16799,10 +16668,6 @@
 					},
 					Bugs: ProtocolBugs{
 						OnlyCompressSecondClientHelloInner: hrr,
-						// Don't duplicate the extension in ClientHelloOuter.
-						ECHOuterExtensionOrder: []uint16{
-							extensionSupportedCurves,
-						},
 					},
 				},
 				flags: []string{
@@ -16812,9 +16677,7 @@
 				},
 				shouldFail:         true,
 				expectedLocalError: "remote error: illegal parameter",
-				// The decoding algorithm relies on the ordering requirement, so
-				// duplicates appear as missing extensions.
-				expectedError: ":OUTER_EXTENSION_NOT_FOUND:",
+				expectedError:      ":DUPLICATE_EXTENSION:",
 			})
 
 			// Test that the server rejects references to missing extensions in
@@ -17030,12 +16893,12 @@
 			})
 		}
 
-		// Test that the ECH server handles a short enc value by falling back to
-		// ClientHelloOuter.
+		// Test that the ECH server handles a short ClientECH.enc value by
+		// falling back to ClientHelloOuter.
 		testCases = append(testCases, testCase{
 			testType: serverTest,
 			protocol: protocol,
-			name:     prefix + "ECH-Server-ShortEnc",
+			name:     prefix + "ECH-Server-ShortClientECHEnc",
 			config: Config{
 				ServerName:      "secret.example",
 				ClientECHConfig: echConfig.ECHConfig,
@@ -17249,54 +17112,6 @@
 			},
 		})
 
-		// Test that the server accepts padding.
-		testCases = append(testCases, testCase{
-			testType: serverTest,
-			protocol: protocol,
-			name:     prefix + "ECH-Server-Padding",
-			config: Config{
-				ClientECHConfig: echConfig.ECHConfig,
-				Bugs: ProtocolBugs{
-					ClientECHPadding: 10,
-				},
-			},
-			flags: []string{
-				"-ech-server-config", base64FlagValue(echConfig.ECHConfig.Raw),
-				"-ech-server-key", base64FlagValue(echConfig.Key),
-				"-ech-is-retry-config", "1",
-				"-expect-ech-accept",
-			},
-			expectations: connectionExpectations{
-				echAccepted: true,
-			},
-		})
-
-		// Test that the server rejects bad padding.
-		testCases = append(testCases, testCase{
-			testType: serverTest,
-			protocol: protocol,
-			name:     prefix + "ECH-Server-BadPadding",
-			config: Config{
-				ClientECHConfig: echConfig.ECHConfig,
-				Bugs: ProtocolBugs{
-					ClientECHPadding:    10,
-					BadClientECHPadding: true,
-				},
-			},
-			flags: []string{
-				"-ech-server-config", base64FlagValue(echConfig.ECHConfig.Raw),
-				"-ech-server-key", base64FlagValue(echConfig.Key),
-				"-ech-is-retry-config", "1",
-				"-expect-ech-accept",
-			},
-			expectations: connectionExpectations{
-				echAccepted: true,
-			},
-			shouldFail:         true,
-			expectedError:      ":DECODE_ERROR",
-			expectedLocalError: "remote error: illegal parameter",
-		})
-
 		// Test the client's behavior when the server ignores ECH GREASE.
 		testCases = append(testCases, testCase{
 			testType: clientTest,
@@ -17363,19 +17178,19 @@
 			flags: []string{"-enable-ech-grease"},
 		})
 
-		// TLS 1.2 ServerHellos cannot contain retry configs.
 		if protocol != quic {
+			// Test that the client rejects retry configs in TLS 1.2.
 			testCases = append(testCases, testCase{
 				testType: clientTest,
 				protocol: protocol,
-				name:     prefix + "ECH-GREASE-Client-TLS12-RejectRetryConfigs",
+				name:     prefix + "ECH-GREASE-Client-TLS12-Retry-Configs",
 				config: Config{
-					MinVersion:       VersionTLS12,
-					MaxVersion:       VersionTLS12,
-					ServerECHConfigs: []ServerECHConfig{echConfig},
+					MinVersion: VersionTLS12,
+					MaxVersion: VersionTLS12,
 					Bugs: ProtocolBugs{
-						ExpectClientECH:           true,
-						AlwaysSendECHRetryConfigs: true,
+						ExpectClientECH:                       true,
+						SendECHRetryConfigs:                   CreateECHConfigList(echConfig.ECHConfig.Raw, unsupportedVersion),
+						SendECHRetryConfigsInTLS12ServerHello: true,
 					},
 				},
 				flags:              []string{"-enable-ech-grease"},
@@ -17383,101 +17198,8 @@
 				expectedLocalError: "remote error: unsupported extension",
 				expectedError:      ":UNEXPECTED_EXTENSION:",
 			})
-			testCases = append(testCases, testCase{
-				testType: clientTest,
-				protocol: protocol,
-				name:     prefix + "ECH-Client-TLS12-RejectRetryConfigs",
-				config: Config{
-					MinVersion:       VersionTLS12,
-					MaxVersion:       VersionTLS12,
-					ServerECHConfigs: []ServerECHConfig{echConfig},
-					Bugs: ProtocolBugs{
-						ExpectClientECH:           true,
-						AlwaysSendECHRetryConfigs: true,
-					},
-				},
-				flags: []string{
-					"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig1.ECHConfig.Raw)),
-				},
-				shouldFail:         true,
-				expectedLocalError: "remote error: unsupported extension",
-				expectedError:      ":UNEXPECTED_EXTENSION:",
-			})
 		}
 
-		// Retry configs must be rejected when ECH is accepted.
-		testCases = append(testCases, testCase{
-			testType: clientTest,
-			protocol: protocol,
-			name:     prefix + "ECH-Client-Accept-RejectRetryConfigs",
-			config: Config{
-				ServerECHConfigs: []ServerECHConfig{echConfig},
-				Bugs: ProtocolBugs{
-					ExpectClientECH:           true,
-					AlwaysSendECHRetryConfigs: true,
-				},
-			},
-			flags: []string{
-				"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig.ECHConfig.Raw)),
-			},
-			shouldFail:         true,
-			expectedLocalError: "remote error: unsupported extension",
-			expectedError:      ":UNEXPECTED_EXTENSION:",
-		})
-
-		// Unsolicited ECH HelloRetryRequest extensions should be rejected.
-		testCases = append(testCases, testCase{
-			testType: clientTest,
-			protocol: protocol,
-			name:     prefix + "ECH-Client-UnsolictedHRRExtension",
-			config: Config{
-				ServerECHConfigs: []ServerECHConfig{echConfig},
-				CurvePreferences: []CurveID{CurveP384},
-				Bugs: ProtocolBugs{
-					AlwaysSendECHHelloRetryRequest: true,
-					ExpectMissingKeyShare:          true, // Check we triggered HRR.
-				},
-			},
-			shouldFail:         true,
-			expectedLocalError: "remote error: unsupported extension",
-			expectedError:      ":UNEXPECTED_EXTENSION:",
-		})
-
-		// GREASE should ignore ECH HelloRetryRequest extensions.
-		testCases = append(testCases, testCase{
-			testType: clientTest,
-			protocol: protocol,
-			name:     prefix + "ECH-Client-GREASE-IgnoreHRRExtension",
-			config: Config{
-				CurvePreferences: []CurveID{CurveP384},
-				Bugs: ProtocolBugs{
-					AlwaysSendECHHelloRetryRequest: true,
-					ExpectMissingKeyShare:          true, // Check we triggered HRR.
-				},
-			},
-			flags: []string{"-enable-ech-grease"},
-		})
-
-		// Random ECH HelloRetryRequest extensions also signal ECH reject.
-		testCases = append(testCases, testCase{
-			testType: clientTest,
-			protocol: protocol,
-			name:     prefix + "ECH-Client-Reject-RandomHRRExtension",
-			config: Config{
-				CurvePreferences: []CurveID{CurveP384},
-				Bugs: ProtocolBugs{
-					AlwaysSendECHHelloRetryRequest: true,
-					ExpectMissingKeyShare:          true, // Check we triggered HRR.
-				},
-			},
-			flags: []string{
-				"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig.ECHConfig.Raw)),
-			},
-			shouldFail:         true,
-			expectedLocalError: "remote error: ECH required",
-			expectedError:      ":ECH_REJECTED:",
-		})
-
 		// Test that the client aborts with a decode_error alert when it receives a
 		// syntactically-invalid encrypted_client_hello extension from the server.
 		testCases = append(testCases, testCase{
@@ -17498,63 +17220,60 @@
 			expectedError:      ":ERROR_PARSING_EXTENSION:",
 		})
 
-		// Test that the server responds to an inner ECH extension with the
+		// Test that the server responds to an empty ech_is_inner extension with the
 		// acceptance confirmation.
 		testCases = append(testCases, testCase{
 			testType: serverTest,
 			protocol: protocol,
-			name:     prefix + "ECH-Server-ECHInner",
+			name:     prefix + "ECH-Server-ECHIsInner",
 			config: Config{
 				MinVersion: VersionTLS13,
 				MaxVersion: VersionTLS13,
 				Bugs: ProtocolBugs{
-					AlwaysSendECHInner: true,
-				},
-			},
-			resumeSession: true,
-		})
-		testCases = append(testCases, testCase{
-			testType: serverTest,
-			protocol: protocol,
-			name:     prefix + "ECH-Server-ECHInner-HelloRetryRequest",
-			config: Config{
-				MinVersion: VersionTLS13,
-				MaxVersion: VersionTLS13,
-				// Force a HelloRetryRequest.
-				DefaultCurves: []CurveID{},
-				Bugs: ProtocolBugs{
-					AlwaysSendECHInner: true,
+					AlwaysSendECHIsInner: true,
 				},
 			},
 			resumeSession: true,
 		})
 
 		// Test that server fails the handshake when it sees a non-empty
-		// inner ECH extension.
+		// ech_is_inner extension.
 		testCases = append(testCases, testCase{
 			testType: serverTest,
 			protocol: protocol,
-			name:     prefix + "ECH-Server-ECHInner-NotEmpty",
+			name:     prefix + "ECH-Server-ECHIsInner-NotEmpty",
 			config: Config{
 				MinVersion: VersionTLS13,
 				MaxVersion: VersionTLS13,
 				Bugs: ProtocolBugs{
-					AlwaysSendECHInner:  true,
-					SendInvalidECHInner: []byte{42, 42, 42},
+					AlwaysSendECHIsInner:  true,
+					SendInvalidECHIsInner: []byte{42, 42, 42},
 				},
 			},
 			shouldFail:         true,
-			expectedLocalError: "remote error: error decoding message",
+			expectedLocalError: "remote error: illegal parameter",
 			expectedError:      ":ERROR_PARSING_EXTENSION:",
 		})
 
-		// Test that a TLS 1.3 server that receives an inner ECH extension can
+		// When ech_is_inner extension is absent, the server should not accept ECH.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			protocol: protocol,
+			name:     prefix + "ECH-Server-ECHIsInner-Absent",
+			config: Config{
+				MinVersion: VersionTLS13,
+				MaxVersion: VersionTLS13,
+			},
+			resumeSession: true,
+		})
+
+		// Test that a TLS 1.3 server that receives an ech_is_inner extension can
 		// negotiate TLS 1.2 without clobbering the downgrade signal.
 		if protocol != quic {
 			testCases = append(testCases, testCase{
 				testType: serverTest,
 				protocol: protocol,
-				name:     prefix + "ECH-Server-ECHInner-Absent-TLS12",
+				name:     prefix + "ECH-Server-ECHIsInner-Absent-TLS12",
 				config: Config{
 					MinVersion: VersionTLS12,
 					MaxVersion: VersionTLS13,
@@ -17562,7 +17281,7 @@
 						// Omit supported_versions extension so the server negotiates
 						// TLS 1.2.
 						OmitSupportedVersions: true,
-						AlwaysSendECHInner:    true,
+						AlwaysSendECHIsInner:  true,
 					},
 				},
 				// Check that the client sees the TLS 1.3 downgrade signal in
@@ -17572,6 +17291,26 @@
 			})
 		}
 
+		// Test that the handshake fails when the server has no ECHConfigs and the
+		// ClientHello contains both encrypted_client_hello and ech_is_inner
+		// extensions.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			protocol: protocol,
+			name:     prefix + "ECH-Server-Disabled-EncryptedClientHello-ECHIsInner",
+			config: Config{
+				MinVersion:      VersionTLS13,
+				MaxVersion:      VersionTLS13,
+				ClientECHConfig: echConfig.ECHConfig,
+				Bugs: ProtocolBugs{
+					AlwaysSendECHIsInner: true,
+				},
+			},
+			shouldFail:         true,
+			expectedLocalError: "remote error: illegal parameter",
+			expectedError:      ":UNEXPECTED_EXTENSION:",
+		})
+
 		// Test the client can negotiate ECH, with and without HelloRetryRequest.
 		testCases = append(testCases, testCase{
 			testType: clientTest,
@@ -18148,7 +17887,9 @@
 		})
 
 		// Test that the client rejects ClientHelloOuter handshakes that attempt
-		// to resume the ClientHelloInner's ticket, at TLS 1.2 and TLS 1.3.
+		// to resume the ClientHelloInner's ticket. In draft-ietf-tls-esni-10,
+		// the confirmation signal is computed in an odd order, so this requires
+		// an explicit check on the client.
 		testCases = append(testCases, testCase{
 			testType: clientTest,
 			protocol: protocol,
@@ -18178,6 +17919,9 @@
 			expectations:       connectionExpectations{echAccepted: true},
 			resumeExpectations: &connectionExpectations{echAccepted: false},
 		})
+
+		// Test the above, but the server now attempts to resume the
+		// ClientHelloInner's ticket at TLS 1.2.
 		if protocol != quic {
 			testCases = append(testCases, testCase{
 				testType: clientTest,
@@ -18597,31 +18341,6 @@
 			expectedError:           ":ECH_REJECTED:",
 			expectedLocalError:      "remote error: ECH required",
 		})
-
-		// Test that the client checks both HelloRetryRequest and ServerHello
-		// for a confirmation signal.
-		testCases = append(testCases, testCase{
-			testType: clientTest,
-			protocol: protocol,
-			name:     prefix + "ECH-Client-HelloRetryRequest-MissingServerHelloConfirmation",
-			config: Config{
-				MinVersion:       VersionTLS13,
-				MaxVersion:       VersionTLS13,
-				CurvePreferences: []CurveID{CurveP384},
-				ServerECHConfigs: []ServerECHConfig{echConfig},
-				Bugs: ProtocolBugs{
-					ExpectMissingKeyShare:          true, // Check we triggered HRR.
-					OmitServerHelloECHConfirmation: true,
-				},
-			},
-			resumeSession: true,
-			flags: []string{
-				"-ech-config-list", base64FlagValue(CreateECHConfigList(echConfig.ECHConfig.Raw)),
-				"-expect-hrr", // Check we triggered HRR.
-			},
-			shouldFail:    true,
-			expectedError: ":INCONSISTENT_ECH_NEGOTIATION:",
-		})
 	}
 }
 
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 7d1cefa..12a9f7a 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -810,7 +810,7 @@
 
   GetTestState(ssl)->cert_verified = true;
   if (config->verify_fail) {
-    X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
+    store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION;
     return 0;
   }
 
diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc
index 226c67b..0354f39 100644
--- a/src/ssl/tls13_both.cc
+++ b/src/ssl/tls13_both.cc
@@ -235,14 +235,15 @@
     }
 
     // Parse out the extensions.
-    SSLExtension status_request(
-        TLSEXT_TYPE_status_request,
-        !ssl->server && hs->config->ocsp_stapling_enabled);
-    SSLExtension sct(
-        TLSEXT_TYPE_certificate_timestamp,
-        !ssl->server && hs->config->signed_cert_timestamps_enabled);
+    bool have_status_request = false, have_sct = false;
+    CBS status_request, sct;
+    const SSL_EXTENSION_TYPE ext_types[] = {
+        {TLSEXT_TYPE_status_request, &have_status_request, &status_request},
+        {TLSEXT_TYPE_certificate_timestamp, &have_sct, &sct},
+    };
+
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!ssl_parse_extensions(&extensions, &alert, {&status_request, &sct},
+    if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                               /*ignore_unknown=*/false)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return false;
@@ -250,14 +251,20 @@
 
     // All Certificate extensions are parsed, but only the leaf extensions are
     // stored.
-    if (status_request.present) {
+    if (have_status_request) {
+      if (ssl->server || !hs->config->ocsp_stapling_enabled) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        return false;
+      }
+
       uint8_t status_type;
       CBS ocsp_response;
-      if (!CBS_get_u8(&status_request.data, &status_type) ||
+      if (!CBS_get_u8(&status_request, &status_type) ||
           status_type != TLSEXT_STATUSTYPE_ocsp ||
-          !CBS_get_u24_length_prefixed(&status_request.data, &ocsp_response) ||
+          !CBS_get_u24_length_prefixed(&status_request, &ocsp_response) ||
           CBS_len(&ocsp_response) == 0 ||
-          CBS_len(&status_request.data) != 0) {
+          CBS_len(&status_request) != 0) {
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         return false;
       }
@@ -272,8 +279,14 @@
       }
     }
 
-    if (sct.present) {
-      if (!ssl_is_sct_list_valid(&sct.data)) {
+    if (have_sct) {
+      if (ssl->server || !hs->config->signed_cert_timestamps_enabled) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        return false;
+      }
+
+      if (!ssl_is_sct_list_valid(&sct)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         return false;
@@ -281,7 +294,7 @@
 
       if (sk_CRYPTO_BUFFER_num(certs.get()) == 1) {
         hs->new_session->signed_cert_timestamp_list.reset(
-            CRYPTO_BUFFER_new_from_CBS(&sct.data, ssl->ctx->pool));
+            CRYPTO_BUFFER_new_from_CBS(&sct, ssl->ctx->pool));
         if (hs->new_session->signed_cert_timestamp_list == nullptr) {
           ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
           return false;
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index af2120c..bff7fb9 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -101,73 +101,6 @@
   return true;
 }
 
-static bool parse_server_hello_tls13(const SSL_HANDSHAKE *hs,
-                                     ParsedServerHello *out, uint8_t *out_alert,
-                                     const SSLMessage &msg) {
-  if (!ssl_parse_server_hello(out, out_alert, msg)) {
-    return false;
-  }
-  // The RFC8446 version of the structure fixes some legacy values.
-  // Additionally, the session ID must echo the original one.
-  if (out->legacy_version != TLS1_2_VERSION ||
-      out->compression_method != 0 ||
-      !CBS_mem_equal(&out->session_id, hs->session_id, hs->session_id_len) ||
-      CBS_len(&out->extensions) == 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return false;
-  }
-  return true;
-}
-
-static bool is_hello_retry_request(const ParsedServerHello &server_hello) {
-  return Span<const uint8_t>(server_hello.random) == kHelloRetryRequest;
-}
-
-static bool check_ech_confirmation(const SSL_HANDSHAKE *hs, bool *out_accepted,
-                                   uint8_t *out_alert,
-                                   const ParsedServerHello &server_hello) {
-  const bool is_hrr = is_hello_retry_request(server_hello);
-  size_t offset;
-  if (is_hrr) {
-    // We check for an unsolicited extension when parsing all of them.
-    SSLExtension ech(TLSEXT_TYPE_encrypted_client_hello);
-    if (!ssl_parse_extensions(&server_hello.extensions, out_alert, {&ech},
-                              /*ignore_unknown=*/true)) {
-      return false;
-    }
-    if (!ech.present) {
-      *out_accepted = false;
-      return true;
-    }
-    if (CBS_len(&ech.data) != ECH_CONFIRMATION_SIGNAL_LEN) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      *out_alert = SSL_AD_DECODE_ERROR;
-      return false;
-    }
-    offset = CBS_data(&ech.data) - CBS_data(&server_hello.raw);
-  } else {
-    offset = ssl_ech_confirmation_signal_hello_offset(hs->ssl);
-  }
-
-  if (!hs->selected_ech_config) {
-    *out_accepted = false;
-    return true;
-  }
-
-  uint8_t expected[ECH_CONFIRMATION_SIGNAL_LEN];
-  if (!ssl_ech_accept_confirmation(hs, expected, hs->inner_client_random,
-                                   hs->inner_transcript, is_hrr,
-                                   server_hello.raw, offset)) {
-    *out_alert = SSL_AD_INTERNAL_ERROR;
-    return false;
-  }
-
-  *out_accepted = CRYPTO_memcmp(CBS_data(&server_hello.raw) + offset, expected,
-                                sizeof(expected)) == 0;
-  return true;
-}
-
 static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   assert(ssl->s3->have_version);
@@ -184,17 +117,36 @@
     return ssl_hs_error;
   }
 
-  ParsedServerHello server_hello;
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
     return ssl_hs_error;
   }
 
-  // The cipher suite must be one we offered. We currently offer all supported
-  // TLS 1.3 ciphers, so check the version.
-  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
-  if (cipher == nullptr ||
+  CBS body = msg.body, extensions, server_random, session_id;
+  uint16_t server_version, cipher_suite;
+  uint8_t compression_method;
+  if (!CBS_get_u16(&body, &server_version) ||
+      !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&body, &session_id) ||
+      !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
+      !CBS_get_u16(&body, &cipher_suite) ||
+      !CBS_get_u8(&body, &compression_method) ||
+      compression_method != 0 ||
+      !CBS_get_u16_length_prefixed(&body, &extensions) ||
+      CBS_len(&extensions) == 0 ||
+      CBS_len(&body) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  if (!CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
+    hs->tls13_state = state_read_server_hello;
+    return ssl_hs_ok;
+  }
+
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
+  // Check if the cipher is a TLS 1.3 cipher.
+  if (cipher == NULL ||
       SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
@@ -204,60 +156,32 @@
 
   hs->new_cipher = cipher;
 
-  const bool is_hrr = is_hello_retry_request(server_hello);
-  if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher) ||
-      (is_hrr && !hs->transcript.UpdateForHelloRetryRequest())) {
-    return ssl_hs_error;
-  }
-  if (hs->selected_ech_config) {
-    if (!hs->inner_transcript.InitHash(ssl_protocol_version(ssl),
-                                       hs->new_cipher) ||
-        (is_hrr && !hs->inner_transcript.UpdateForHelloRetryRequest())) {
-      return ssl_hs_error;
-    }
-  }
+  bool have_cookie, have_key_share, have_supported_versions;
+  CBS cookie, key_share, supported_versions;
+  SSL_EXTENSION_TYPE ext_types[] = {
+      {TLSEXT_TYPE_key_share, &have_key_share, &key_share},
+      {TLSEXT_TYPE_cookie, &have_cookie, &cookie},
+      {TLSEXT_TYPE_supported_versions, &have_supported_versions,
+       &supported_versions},
+  };
 
-  // Determine which ClientHello the server is responding to. Run
-  // |check_ech_confirmation| unconditionally, so we validate the extension
-  // contents.
-  bool ech_accepted;
-  if (!check_ech_confirmation(hs, &ech_accepted, &alert, server_hello)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
-    return ssl_hs_error;
-  }
-  if (hs->selected_ech_config) {
-    ssl->s3->ech_status = ech_accepted ? ssl_ech_accepted : ssl_ech_rejected;
-  }
-
-  if (!is_hrr) {
-    hs->tls13_state = state_read_server_hello;
-    return ssl_hs_ok;
-  }
-
-  // The ECH extension, if present, was already parsed by
-  // |check_ech_confirmation|.
-  SSLExtension cookie(TLSEXT_TYPE_cookie), key_share(TLSEXT_TYPE_key_share),
-      supported_versions(TLSEXT_TYPE_supported_versions),
-      ech_unused(TLSEXT_TYPE_encrypted_client_hello,
-                 hs->selected_ech_config || hs->config->ech_grease_enabled);
-  if (!ssl_parse_extensions(
-          &server_hello.extensions, &alert,
-          {&cookie, &key_share, &supported_versions, &ech_unused},
-          /*ignore_unknown=*/false)) {
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+                            /*ignore_unknown=*/false)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  if (!cookie.present && !key_share.present) {
+  if (!have_cookie && !have_key_share) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_EMPTY_HELLO_RETRY_REQUEST);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
-  if (cookie.present) {
+  if (have_cookie) {
     CBS cookie_value;
-    if (!CBS_get_u16_length_prefixed(&cookie.data, &cookie_value) ||
+    if (!CBS_get_u16_length_prefixed(&cookie, &cookie_value) ||
         CBS_len(&cookie_value) == 0 ||
-        CBS_len(&cookie.data) != 0) {
+        CBS_len(&cookie) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
@@ -268,10 +192,9 @@
     }
   }
 
-  if (key_share.present) {
+  if (have_key_share) {
     uint16_t group_id;
-    if (!CBS_get_u16(&key_share.data, &group_id) ||
-        CBS_len(&key_share.data) != 0) {
+    if (!CBS_get_u16(&key_share, &group_id) || CBS_len(&key_share) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
@@ -298,16 +221,23 @@
     }
   }
 
-  // Although we now know whether ClientHelloInner was used, we currently
-  // maintain both transcripts up to ServerHello. We could swap transcripts
-  // early, but then ClientHello construction and |check_ech_confirmation|
-  // become more complex.
-  if (!ssl_hash_message(hs, msg)) {
+  // We do not know whether ECH was chosen until ServerHello and must
+  // concurrently update both transcripts.
+  //
+  // TODO(https://crbug.com/boringssl/275): A later draft will likely add an ECH
+  // signal to HRR and change this.
+  if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher) ||
+      !hs->transcript.UpdateForHelloRetryRequest() ||
+      !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
-  if (ssl->s3->ech_status == ssl_ech_accepted &&
-      !hs->inner_transcript.Update(msg.raw)) {
-    return ssl_hs_error;
+  if (hs->selected_ech_config) {
+    if (!hs->inner_transcript.InitHash(ssl_protocol_version(ssl),
+                                       hs->new_cipher) ||
+        !hs->inner_transcript.UpdateForHelloRetryRequest() ||
+        !hs->inner_transcript.Update(msg.raw)) {
+      return ssl_hs_error;
+    }
   }
 
   // HelloRetryRequest should be the end of the flight.
@@ -337,8 +267,7 @@
 
   // Build the second ClientHelloInner, if applicable. The second ClientHello
   // uses an empty string for |enc|.
-  if (hs->ssl->s3->ech_status == ssl_ech_accepted &&
-      !ssl_encrypt_client_hello(hs, {})) {
+  if (hs->selected_ech_config && !ssl_encrypt_client_hello(hs, {})) {
     return ssl_hs_error;
   }
 
@@ -357,70 +286,83 @@
   if (!ssl->method->get_message(ssl, &msg)) {
     return ssl_hs_read_message;
   }
-  ParsedServerHello server_hello;
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
+    return ssl_hs_error;
+  }
+
+  CBS body = msg.body, server_random, session_id, extensions;
+  uint16_t server_version;
+  uint16_t cipher_suite;
+  uint8_t compression_method;
+  if (!CBS_get_u16(&body, &server_version) ||
+      !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&body, &session_id) ||
+      !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
+      !CBS_get_u16(&body, &cipher_suite) ||
+      !CBS_get_u8(&body, &compression_method) ||
+      compression_method != 0 ||
+      !CBS_get_u16_length_prefixed(&body, &extensions) ||
+      CBS_len(&body) != 0) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return ssl_hs_error;
+  }
+
+  if (server_version != TLS1_2_VERSION) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
     return ssl_hs_error;
   }
 
   // Forbid a second HelloRetryRequest.
-  if (is_hello_retry_request(server_hello)) {
+  if (CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     return ssl_hs_error;
   }
 
-  // Check the cipher suite, in case this is after HelloRetryRequest.
-  if (SSL_CIPHER_get_value(hs->new_cipher) != server_hello.cipher_suite) {
+  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
+                 SSL3_RANDOM_SIZE);
+
+  // Check if the cipher is a TLS 1.3 cipher.
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
+  if (cipher == nullptr ||
+      SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
+      SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
-  if (ssl->s3->ech_status == ssl_ech_accepted) {
-    if (ssl->s3->used_hello_retry_request) {
-      // HelloRetryRequest and ServerHello must accept ECH consistently.
-      bool ech_accepted;
-      if (!check_ech_confirmation(hs, &ech_accepted, &alert, server_hello)) {
-        ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
-        return ssl_hs_error;
-      }
-      if (!ech_accepted) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_INCONSISTENT_ECH_NEGOTIATION);
-        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-        return ssl_hs_error;
-      }
-    }
-
-    hs->transcript = std::move(hs->inner_transcript);
-    hs->extensions.sent = hs->inner_extensions_sent;
-    // Report the inner random value through |SSL_get_client_random|.
-    OPENSSL_memcpy(ssl->s3->client_random, hs->inner_client_random,
-                   SSL3_RANDOM_SIZE);
+  // Check that the cipher matches the one in the HelloRetryRequest.
+  if (ssl->s3->used_hello_retry_request && hs->new_cipher != cipher) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return ssl_hs_error;
   }
 
-  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random),
-                 SSL3_RANDOM_SIZE);
+  // Parse out the extensions.
+  bool have_key_share = false, have_pre_shared_key = false,
+       have_supported_versions = false;
+  CBS key_share, pre_shared_key, supported_versions;
+  SSL_EXTENSION_TYPE ext_types[] = {
+      {TLSEXT_TYPE_key_share, &have_key_share, &key_share},
+      {TLSEXT_TYPE_pre_shared_key, &have_pre_shared_key, &pre_shared_key},
+      {TLSEXT_TYPE_supported_versions, &have_supported_versions,
+       &supported_versions},
+  };
 
-  // When offering ECH, |ssl->session| is only offered in ClientHelloInner.
-  const bool pre_shared_key_allowed =
-      ssl->session != nullptr && ssl->s3->ech_status != ssl_ech_rejected;
-  SSLExtension key_share(TLSEXT_TYPE_key_share),
-      pre_shared_key(TLSEXT_TYPE_pre_shared_key, pre_shared_key_allowed),
-      supported_versions(TLSEXT_TYPE_supported_versions);
-  if (!ssl_parse_extensions(&server_hello.extensions, &alert,
-                            {&key_share, &pre_shared_key, &supported_versions},
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             /*ignore_unknown=*/false)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  // Recheck supported_versions, in case this is after HelloRetryRequest.
+  // Recheck supported_versions, in case this is the second ServerHello.
   uint16_t version;
-  if (!supported_versions.present ||
-      !CBS_get_u16(&supported_versions.data, &version) ||
-      CBS_len(&supported_versions.data) != 0 ||
+  if (!have_supported_versions ||
+      !CBS_get_u16(&supported_versions, &version) ||
       version != ssl->version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SECOND_SERVERHELLO_VERSION_MISMATCH);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -428,9 +370,15 @@
   }
 
   alert = SSL_AD_DECODE_ERROR;
-  if (pre_shared_key.present) {
+  if (have_pre_shared_key) {
+    if (ssl->session == NULL) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+      return ssl_hs_error;
+    }
+
     if (!ssl_ext_pre_shared_key_parse_serverhello(hs, &alert,
-                                                  &pre_shared_key.data)) {
+                                                  &pre_shared_key)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
@@ -441,7 +389,7 @@
       return ssl_hs_error;
     }
 
-    if (ssl->session->cipher->algorithm_prf != hs->new_cipher->algorithm_prf) {
+    if (ssl->session->cipher->algorithm_prf != cipher->algorithm_prf) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_PRF_HASH_MISMATCH);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
@@ -474,11 +422,13 @@
     return ssl_hs_error;
   }
 
-  hs->new_session->cipher = hs->new_cipher;
+  hs->new_session->cipher = cipher;
+  hs->new_cipher = cipher;
+
+  size_t hash_len =
+      EVP_MD_size(ssl_get_handshake_digest(ssl_protocol_version(ssl), cipher));
 
   // Set up the key schedule and incorporate the PSK into the running secret.
-  size_t hash_len = EVP_MD_size(
-      ssl_get_handshake_digest(ssl_protocol_version(ssl), hs->new_cipher));
   if (!tls13_init_key_schedule(
           hs, ssl->s3->session_reused
                   ? MakeConstSpan(hs->new_session->secret,
@@ -487,7 +437,7 @@
     return ssl_hs_error;
   }
 
-  if (!key_share.present) {
+  if (!have_key_share) {
     // We do not support psk_ke and thus always require a key share.
     OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
@@ -498,13 +448,53 @@
   Array<uint8_t> dhe_secret;
   alert = SSL_AD_DECODE_ERROR;
   if (!ssl_ext_key_share_parse_serverhello(hs, &dhe_secret, &alert,
-                                           &key_share.data)) {
+                                           &key_share)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  if (!tls13_advance_key_schedule(hs, dhe_secret) ||
-      !ssl_hash_message(hs, msg) ||
+  if (!tls13_advance_key_schedule(hs, dhe_secret)) {
+    return ssl_hs_error;
+  }
+
+  // Determine whether the server accepted ECH.
+  //
+  // TODO(https://crbug.com/boringssl/275): This is a bit late in the process of
+  // parsing ServerHello. |ssl->session| is only valid for ClientHelloInner, so
+  // the decisions made based on PSK need to be double-checked. draft-11 will
+  // fix this, at which point this logic can be moved before any processing.
+  if (hs->selected_ech_config) {
+    uint8_t ech_confirmation[ECH_CONFIRMATION_SIGNAL_LEN];
+    if (!hs->inner_transcript.InitHash(ssl_protocol_version(ssl),
+                                       hs->new_cipher) ||
+        !ssl_ech_accept_confirmation(hs, ech_confirmation, hs->inner_transcript,
+                                     msg.raw)) {
+      return ssl_hs_error;
+    }
+
+    if (CRYPTO_memcmp(ech_confirmation,
+                      ssl->s3->server_random + sizeof(ssl->s3->server_random) -
+                          sizeof(ech_confirmation),
+                      sizeof(ech_confirmation)) == 0) {
+      ssl->s3->ech_status = ssl_ech_accepted;
+      hs->transcript = std::move(hs->inner_transcript);
+      hs->extensions.sent = hs->inner_extensions_sent;
+      // Report the inner random value through |SSL_get_client_random|.
+      OPENSSL_memcpy(ssl->s3->client_random, hs->inner_client_random,
+                     SSL3_RANDOM_SIZE);
+    } else {
+      // Resuming against the ClientHelloOuter was an unsolicited extension.
+      if (have_pre_shared_key) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        return ssl_hs_error;
+      }
+      ssl->s3->ech_status = ssl_ech_rejected;
+    }
+  }
+
+
+  if (!ssl_hash_message(hs, msg) ||
       !tls13_derive_handshake_secrets(hs)) {
     return ssl_hs_error;
   }
@@ -542,16 +532,14 @@
     return ssl_hs_error;
   }
 
-  CBS body = msg.body, extensions;
-  if (!CBS_get_u16_length_prefixed(&body, &extensions) ||
-      CBS_len(&body) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+  CBS body = msg.body;
+  if (!ssl_parse_serverhello_tlsext(hs, &body)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     return ssl_hs_error;
   }
-
-  if (!ssl_parse_serverhello_tlsext(hs, &extensions)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+  if (CBS_len(&body) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
@@ -638,19 +626,25 @@
   }
 
 
-  SSLExtension sigalgs(TLSEXT_TYPE_signature_algorithms),
-      ca(TLSEXT_TYPE_certificate_authorities);
+  bool have_sigalgs = false, have_ca = false;
+  CBS sigalgs, ca;
+  const SSL_EXTENSION_TYPE ext_types[] = {
+    {TLSEXT_TYPE_signature_algorithms, &have_sigalgs, &sigalgs},
+    {TLSEXT_TYPE_certificate_authorities, &have_ca, &ca},
+  };
+
   CBS body = msg.body, context, extensions, supported_signature_algorithms;
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!CBS_get_u8_length_prefixed(&body, &context) ||
       // The request context is always empty during the handshake.
       CBS_len(&context) != 0 ||
-      !CBS_get_u16_length_prefixed(&body, &extensions) ||  //
+      !CBS_get_u16_length_prefixed(&body, &extensions) ||
       CBS_len(&body) != 0 ||
-      !ssl_parse_extensions(&extensions, &alert, {&sigalgs, &ca},
+      !ssl_parse_extensions(&extensions, &alert, ext_types,
                             /*ignore_unknown=*/true) ||
-      !sigalgs.present ||
-      !CBS_get_u16_length_prefixed(&sigalgs.data,
+      (have_ca && CBS_len(&ca) == 0) ||
+      !have_sigalgs ||
+      !CBS_get_u16_length_prefixed(&sigalgs,
                                    &supported_signature_algorithms) ||
       !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -658,8 +652,8 @@
     return ssl_hs_error;
   }
 
-  if (ca.present) {
-    hs->ca_names = ssl_parse_client_CA_list(ssl, &alert, &ca.data);
+  if (have_ca) {
+    hs->ca_names = ssl_parse_client_CA_list(ssl, &alert, &ca);
     if (!hs->ca_names) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
@@ -1082,17 +1076,23 @@
     return nullptr;
   }
 
-  SSLExtension early_data(TLSEXT_TYPE_early_data);
+  // Parse out the extensions.
+  bool have_early_data = false;
+  CBS early_data;
+  const SSL_EXTENSION_TYPE ext_types[] = {
+      {TLSEXT_TYPE_early_data, &have_early_data, &early_data},
+  };
+
   uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_extensions(&extensions, &alert, {&early_data},
+  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                             /*ignore_unknown=*/true)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return nullptr;
   }
 
-  if (early_data.present) {
-    if (!CBS_get_u32(&early_data.data, &session->ticket_max_early_data) ||
-        CBS_len(&early_data.data) != 0) {
+  if (have_early_data) {
+    if (!CBS_get_u32(&early_data, &session->ticket_max_early_data) ||
+        CBS_len(&early_data) != 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       return nullptr;
diff --git a/src/ssl/tls13_enc.cc b/src/ssl/tls13_enc.cc
index c7b75a6..174f5f1 100644
--- a/src/ssl/tls13_enc.cc
+++ b/src/ssl/tls13_enc.cc
@@ -489,8 +489,8 @@
     return false;
   }
 
-  auto msg_binder = msg.last(verify_data_len);
-  OPENSSL_memcpy(msg_binder.data(), verify_data, verify_data_len);
+  OPENSSL_memcpy(msg.data() + msg.size() - verify_data_len, verify_data,
+                 verify_data_len);
   if (out_binder_len != nullptr) {
     *out_binder_len = verify_data_len;
   }
@@ -537,46 +537,57 @@
          ECH_CONFIRMATION_SIGNAL_LEN;
 }
 
-bool ssl_ech_accept_confirmation(const SSL_HANDSHAKE *hs, Span<uint8_t> out,
-                                 Span<const uint8_t> client_random,
-                                 const SSLTranscript &transcript, bool is_hrr,
-                                 Span<const uint8_t> msg, size_t offset) {
-  // See draft-ietf-tls-esni-13, sections 7.2 and 7.2.1.
-  static const uint8_t kZeros[EVP_MAX_MD_SIZE] = {0};
-
-  // We hash |msg|, with bytes from |offset| zeroed.
-  if (msg.size() < offset + ECH_CONFIRMATION_SIGNAL_LEN) {
+bool ssl_ech_accept_confirmation(
+    const SSL_HANDSHAKE *hs, bssl::Span<uint8_t> out,
+    const SSLTranscript &transcript,
+    bssl::Span<const uint8_t> server_hello) {
+  // We hash |server_hello|, with the last |ECH_CONFIRMATION_SIGNAL_LEN| bytes
+  // of the random value zeroed.
+  static const uint8_t kZeroes[ECH_CONFIRMATION_SIGNAL_LEN] = {0};
+  const size_t offset = ssl_ech_confirmation_signal_hello_offset(hs->ssl);
+  if (server_hello.size() < offset + ECH_CONFIRMATION_SIGNAL_LEN) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
   }
 
-  auto before_zeros = msg.subspan(0, offset);
-  auto after_zeros = msg.subspan(offset + ECH_CONFIRMATION_SIGNAL_LEN);
-  uint8_t context[EVP_MAX_MD_SIZE];
-  unsigned context_len;
+  auto before_zeroes = server_hello.subspan(0, offset);
+  auto after_zeroes =
+      server_hello.subspan(offset + ECH_CONFIRMATION_SIGNAL_LEN);
+  uint8_t context_hash[EVP_MAX_MD_SIZE];
+  unsigned context_hash_len;
   ScopedEVP_MD_CTX ctx;
   if (!transcript.CopyToHashContext(ctx.get(), transcript.Digest()) ||
-      !EVP_DigestUpdate(ctx.get(), before_zeros.data(), before_zeros.size()) ||
-      !EVP_DigestUpdate(ctx.get(), kZeros, ECH_CONFIRMATION_SIGNAL_LEN) ||
-      !EVP_DigestUpdate(ctx.get(), after_zeros.data(), after_zeros.size()) ||
-      !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
+      !EVP_DigestUpdate(ctx.get(), before_zeroes.data(),
+                        before_zeroes.size()) ||
+      !EVP_DigestUpdate(ctx.get(), kZeroes, sizeof(kZeroes)) ||
+      !EVP_DigestUpdate(ctx.get(), after_zeroes.data(), after_zeroes.size()) ||
+      !EVP_DigestFinal_ex(ctx.get(), context_hash, &context_hash_len)) {
     return false;
   }
 
-  uint8_t secret[EVP_MAX_MD_SIZE];
-  size_t secret_len;
-  if (!HKDF_extract(secret, &secret_len, transcript.Digest(),
-                    client_random.data(), client_random.size(), kZeros,
-                    transcript.DigestLen())) {
+  // Per draft-ietf-tls-esni-10, accept_confirmation is computed with
+  // Derive-Secret, which derives a secret of size Hash.length. That value is
+  // then truncated to the first 8 bytes. Note this differs from deriving an
+  // 8-byte secret because the target length is included in the derivation.
+  //
+  // TODO(https://crbug.com/boringssl/275): draft-11 will avoid this.
+  uint8_t accept_confirmation_buf[EVP_MAX_MD_SIZE];
+  bssl::Span<uint8_t> accept_confirmation =
+      MakeSpan(accept_confirmation_buf, transcript.DigestLen());
+  if (!hkdf_expand_label(accept_confirmation, transcript.Digest(),
+                         hs->secret(), label_to_span("ech accept confirmation"),
+                         MakeConstSpan(context_hash, context_hash_len))) {
     return false;
   }
 
-  assert(out.size() == ECH_CONFIRMATION_SIGNAL_LEN);
-  return hkdf_expand_label(out, transcript.Digest(),
-                           MakeConstSpan(secret, secret_len),
-                           is_hrr ? label_to_span("hrr ech accept confirmation")
-                                  : label_to_span("ech accept confirmation"),
-                           MakeConstSpan(context, context_len));
+  static_assert(ECH_CONFIRMATION_SIGNAL_LEN < EVP_MAX_MD_SIZE,
+                "ECH confirmation signal too big");
+  if (out.size() != ECH_CONFIRMATION_SIGNAL_LEN) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  OPENSSL_memcpy(out.data(), accept_confirmation.data(), out.size());
+  return true;
 }
 
 BSSL_NAMESPACE_END
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index 2f000e5..501d01f 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -246,7 +246,8 @@
     return ssl_hs_error;
   }
 
-  // The PRF hash is now known.
+  // The PRF hash is now known. Set up the key schedule and hash the
+  // ClientHello.
   if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher)) {
     return ssl_hs_error;
   }
@@ -269,7 +270,7 @@
     return ssl_ticket_aead_ignore_ticket;
   }
 
-  // Per RFC 8446, section 4.2.9, servers MUST abort the handshake if the client
+  // Per RFC8446, section 4.2.9, servers MUST abort the handshake if the client
   // sends pre_shared_key without psk_key_exchange_modes.
   CBS unused;
   if (!ssl_client_hello_get_extension(client_hello, &unused,
@@ -570,34 +571,12 @@
       !CBB_add_u16(&extensions, ssl->version) ||
       !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16(&extensions, 2 /* length */) ||
-      !CBB_add_u16(&extensions, group_id)) {
+      !CBB_add_u16(&extensions, group_id) ||
+      !ssl_add_message_cbb(ssl, cbb.get())) {
     return ssl_hs_error;
   }
-  if (hs->ech_is_inner) {
-    // Fill a placeholder for the ECH confirmation value.
-    if (!CBB_add_u16(&extensions, TLSEXT_TYPE_encrypted_client_hello) ||
-        !CBB_add_u16(&extensions, ECH_CONFIRMATION_SIGNAL_LEN) ||
-        !CBB_add_zeros(&extensions, ECH_CONFIRMATION_SIGNAL_LEN)) {
-      return ssl_hs_error;
-    }
-  }
-  Array<uint8_t> hrr;
-  if (!ssl->method->finish_message(ssl, cbb.get(), &hrr)) {
-    return ssl_hs_error;
-  }
-  if (hs->ech_is_inner) {
-    // Now that the message is encoded, fill in the whole value.
-    size_t offset = hrr.size() - ECH_CONFIRMATION_SIGNAL_LEN;
-    if (!ssl_ech_accept_confirmation(
-            hs, MakeSpan(hrr).last(ECH_CONFIRMATION_SIGNAL_LEN),
-            ssl->s3->client_random, hs->transcript, /*is_hrr=*/true, hrr,
-            offset)) {
-      return ssl_hs_error;
-    }
-  }
 
-  if (!ssl->method->add_message(ssl, std::move(hrr)) ||
-      !ssl->method->add_change_cipher_spec(ssl)) {
+  if (!ssl->method->add_change_cipher_spec(ssl)) {
     return ssl_hs_error;
   }
 
@@ -623,8 +602,8 @@
   }
 
   if (ssl->s3->ech_status == ssl_ech_accepted) {
-    // If we previously accepted the ClientHelloInner, the second ClientHello
-    // must contain an outer encrypted_client_hello extension.
+    // If we previously accepted the ClientHelloInner, check that the second
+    // ClientHello contains an encrypted_client_hello extension.
     CBS ech_body;
     if (!ssl_client_hello_get_extension(&client_hello, &ech_body,
                                         TLSEXT_TYPE_encrypted_client_hello)) {
@@ -632,12 +611,12 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
       return ssl_hs_error;
     }
+
+    // Parse a ClientECH out of the extension body.
     uint16_t kdf_id, aead_id;
-    uint8_t type, config_id;
+    uint8_t config_id;
     CBS enc, payload;
-    if (!CBS_get_u8(&ech_body, &type) ||     //
-        type != ECH_CLIENT_OUTER ||          //
-        !CBS_get_u16(&ech_body, &kdf_id) ||  //
+    if (!CBS_get_u16(&ech_body, &kdf_id) ||  //
         !CBS_get_u16(&ech_body, &aead_id) ||
         !CBS_get_u8(&ech_body, &config_id) ||
         !CBS_get_u16_length_prefixed(&ech_body, &enc) ||
@@ -648,6 +627,8 @@
       return ssl_hs_error;
     }
 
+    // Check that ClientECH.cipher_suite is unchanged and that
+    // ClientECH.enc is empty.
     if (kdf_id != EVP_HPKE_KDF_id(EVP_HPKE_CTX_kdf(hs->ech_hpke_ctx.get())) ||
         aead_id !=
             EVP_HPKE_AEAD_id(EVP_HPKE_CTX_aead(hs->ech_hpke_ctx.get())) ||
@@ -660,9 +641,9 @@
     // Decrypt the payload with the HPKE context from the first ClientHello.
     Array<uint8_t> encoded_client_hello_inner;
     bool unused;
-    if (!ssl_client_hello_decrypt(hs->ech_hpke_ctx.get(),
-                                  &encoded_client_hello_inner, &unused,
-                                  &client_hello, payload)) {
+    if (!ssl_client_hello_decrypt(
+            hs->ech_hpke_ctx.get(), &encoded_client_hello_inner, &unused,
+            &client_hello, kdf_id, aead_id, config_id, enc, payload)) {
       // Decryption failure is fatal in the second ClientHello.
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
@@ -780,18 +761,18 @@
     return ssl_hs_error;
   }
 
-  assert(ssl->s3->ech_status != ssl_ech_accepted || hs->ech_is_inner);
-  if (hs->ech_is_inner) {
+  assert(ssl->s3->ech_status != ssl_ech_accepted || hs->ech_is_inner_present);
+  if (hs->ech_is_inner_present) {
     // Fill in the ECH confirmation signal.
-    const size_t offset = ssl_ech_confirmation_signal_hello_offset(ssl);
-    Span<uint8_t> random_suffix = random.last(ECH_CONFIRMATION_SIGNAL_LEN);
-    if (!ssl_ech_accept_confirmation(hs, random_suffix, ssl->s3->client_random,
-                                     hs->transcript,
-                                     /*is_hrr=*/false, server_hello, offset)) {
+    Span<uint8_t> random_suffix =
+        random.subspan(SSL3_RANDOM_SIZE - ECH_CONFIRMATION_SIGNAL_LEN);
+    if (!ssl_ech_accept_confirmation(hs, random_suffix, hs->transcript,
+                                     server_hello)) {
       return ssl_hs_error;
     }
 
     // Update |server_hello|.
+    const size_t offset = ssl_ech_confirmation_signal_hello_offset(ssl);
     Span<uint8_t> server_hello_out =
         MakeSpan(server_hello).subspan(offset, ECH_CONFIRMATION_SIGNAL_LEN);
     OPENSSL_memcpy(server_hello_out.data(), random_suffix.data(),
@@ -1060,15 +1041,20 @@
       return ssl_hs_error;
     }
 
-    SSLExtension application_settings(TLSEXT_TYPE_application_settings);
+    // Parse out the extensions.
+    bool have_application_settings = false;
+    CBS application_settings;
+    SSL_EXTENSION_TYPE ext_types[] = {{TLSEXT_TYPE_application_settings,
+                                       &have_application_settings,
+                                       &application_settings}};
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!ssl_parse_extensions(&extensions, &alert, {&application_settings},
+    if (!ssl_parse_extensions(&extensions, &alert, ext_types,
                               /*ignore_unknown=*/false)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
 
-    if (!application_settings.present) {
+    if (!have_application_settings) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
       return ssl_hs_error;
@@ -1077,7 +1063,7 @@
     // Note that, if 0-RTT was accepted, these values will already have been
     // initialized earlier.
     if (!hs->new_session->peer_application_settings.CopyFrom(
-            application_settings.data) ||
+            application_settings) ||
         !ssl_hash_message(hs, msg)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
diff --git a/src/tool/generate_ech.cc b/src/tool/generate_ech.cc
index f1e8cbe..1fe501d 100644
--- a/src/tool/generate_ech.cc
+++ b/src/tool/generate_ech.cc
@@ -14,7 +14,6 @@
 
 #include <stdio.h>
 
-#include <limits>
 #include <vector>
 
 #include <openssl/bytestring.h>
diff --git a/src/tool/speed.cc b/src/tool/speed.cc
index b91a4ce..2d01f4b 100644
--- a/src/tool/speed.cc
+++ b/src/tool/speed.cc
@@ -26,7 +26,6 @@
 
 #include <openssl/aead.h>
 #include <openssl/aes.h>
-#include <openssl/base64.h>
 #include <openssl/bn.h>
 #include <openssl/curve25519.h>
 #include <openssl/crypto.h>
@@ -271,16 +270,6 @@
       return false;
     }
     results.Print(name + " verify (fresh key)");
-
-    if (!TimeFunction(&results, [&]() -> bool {
-          return bssl::UniquePtr<RSA>(RSA_private_key_from_bytes(
-                     kRSAKeys[i].key, kRSAKeys[i].key_len)) != nullptr;
-        })) {
-      fprintf(stderr, "Failed to parse %s key.\n", name.c_str());
-      ERR_print_errors_fp(stderr);
-      return false;
-    }
-    results.Print(name + " private key parse");
   }
 
   return true;
@@ -993,48 +982,6 @@
   return true;
 }
 
-static bool SpeedBase64(const std::string &selected) {
-  if (!selected.empty() && selected.find("base64") == std::string::npos) {
-    return true;
-  }
-
-  static const char kInput[] =
-    "MIIDtTCCAp2gAwIBAgIJALW2IrlaBKUhMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV"
-    "BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX"
-    "aWRnaXRzIFB0eSBMdGQwHhcNMTYwNzA5MDQzODA5WhcNMTYwODA4MDQzODA5WjBF"
-    "MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50"
-    "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB"
-    "CgKCAQEAugvahBkSAUF1fC49vb1bvlPrcl80kop1iLpiuYoz4Qptwy57+EWssZBc"
-    "HprZ5BkWf6PeGZ7F5AX1PyJbGHZLqvMCvViP6pd4MFox/igESISEHEixoiXCzepB"
-    "rhtp5UQSjHD4D4hKtgdMgVxX+LRtwgW3mnu/vBu7rzpr/DS8io99p3lqZ1Aky+aN"
-    "lcMj6MYy8U+YFEevb/V0lRY9oqwmW7BHnXikm/vi6sjIS350U8zb/mRzYeIs2R65"
-    "LUduTL50+UMgat9ocewI2dv8aO9Dph+8NdGtg8LFYyTTHcUxJoMr1PTOgnmET19W"
-    "JH4PrFwk7ZE1QJQQ1L4iKmPeQistuQIDAQABo4GnMIGkMB0GA1UdDgQWBBT5m6Vv"
-    "zYjVYHG30iBE+j2XDhUE8jB1BgNVHSMEbjBsgBT5m6VvzYjVYHG30iBE+j2XDhUE"
-    "8qFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV"
-    "BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJALW2IrlaBKUhMAwGA1UdEwQF"
-    "MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAD7Jg68SArYWlcoHfZAB90Pmyrt5H6D8"
-    "LRi+W2Ri1fBNxREELnezWJ2scjl4UMcsKYp4Pi950gVN+62IgrImcCNvtb5I1Cfy"
-    "/MNNur9ffas6X334D0hYVIQTePyFk3umI+2mJQrtZZyMPIKSY/sYGQHhGGX6wGK+"
-    "GO/og0PQk/Vu6D+GU2XRnDV0YZg1lsAsHd21XryK6fDmNkEMwbIWrts4xc7scRrG"
-    "HWy+iMf6/7p/Ak/SIicM4XSwmlQ8pPxAZPr+E2LoVd9pMpWUwpW2UbtO5wsGTrY5"
-    "sO45tFNN/y+jtUheB1C2ijObG/tXELaiyCdM+S/waeuv0MXtI4xnn1A=";
-
-  std::vector<uint8_t> out(strlen(kInput));
-  size_t len;
-  TimeResults results;
-  if (!TimeFunction(&results, [&]() -> bool {
-        return EVP_DecodeBase64(out.data(), &len, out.size(),
-                                reinterpret_cast<const uint8_t *>(kInput),
-                                strlen(kInput));
-      })) {
-    fprintf(stderr, "base64 decode failed.\n");
-    return false;
-  }
-  results.PrintWithBytes("base64 decode", strlen(kInput));
-  return true;
-}
-
 static TRUST_TOKEN_PRETOKEN *trust_token_pretoken_dup(
     TRUST_TOKEN_PRETOKEN *in) {
   TRUST_TOKEN_PRETOKEN *out =
@@ -1433,8 +1380,7 @@
       !SpeedTrustToken("TrustToken-Exp2PMB-Batch1",
                        TRUST_TOKEN_experiment_v2_pmb(), 1, selected) ||
       !SpeedTrustToken("TrustToken-Exp2PMB-Batch10",
-                       TRUST_TOKEN_experiment_v2_pmb(), 10, selected) ||
-      !SpeedBase64(selected)) {
+                       TRUST_TOKEN_experiment_v2_pmb(), 10, selected)) {
     return false;
   }
 #if defined(BORINGSSL_FIPS)
diff --git a/src/tool/tool.cc b/src/tool/tool.cc
index 96bde33..7bec7a2 100644
--- a/src/tool/tool.cc
+++ b/src/tool/tool.cc
@@ -24,7 +24,6 @@
 #include <io.h>
 #else
 #include <libgen.h>
-#include <signal.h>
 #endif
 
 #include "internal.h"
@@ -107,8 +106,6 @@
     perror("_setmode(_fileno(stderr), O_BINARY)");
     return 1;
   }
-#else
-  signal(SIGPIPE, SIG_IGN);
 #endif
 
   CRYPTO_library_init();
diff --git a/src/util/doc.go b/src/util/doc.go
index a38e078..17d7479 100644
--- a/src/util/doc.go
+++ b/src/util/doc.go
@@ -565,28 +565,6 @@
 	return s
 }
 
-var rfcRegexp = regexp.MustCompile("RFC ([0-9]+)")
-
-func markupRFC(html template.HTML) template.HTML {
-	s := string(html)
-	matches := rfcRegexp.FindAllStringSubmatchIndex(s, -1)
-	if len(matches) == 0 {
-		return html
-	}
-
-	var b strings.Builder
-	var idx int
-	for _, match := range matches {
-		start, end := match[0], match[1]
-		number := s[match[2]:match[3]]
-		b.WriteString(s[idx:start])
-		fmt.Fprintf(&b, "<a href=\"https://www.rfc-editor.org/rfc/rfc%s.html\">%s</a>", number, s[start:end])
-		idx = end
-	}
-	b.WriteString(s[idx:])
-	return template.HTML(b.String())
-}
-
 func newlinesToBR(html template.HTML) template.HTML {
 	s := string(html)
 	if !strings.Contains(s, "\n") {
@@ -605,7 +583,6 @@
 		"firstSentence":   firstSentence,
 		"markupPipeWords": func(s string) template.HTML { return markupPipeWords(allDecls, s) },
 		"markupFirstWord": markupFirstWord,
-		"markupRFC":       markupRFC,
 		"newlinesToBR":    newlinesToBR,
 	})
 	headerTmpl, err := headerTmpl.Parse(`<!DOCTYPE html>
@@ -648,7 +625,7 @@
         {{range .Decls}}
           <div class="decl" {{if .Anchor}}id="{{.Anchor}}"{{end}}>
           {{range .Comment}}
-            <p>{{. | markupPipeWords | newlinesToBR | markupFirstWord | markupRFC}}</p>
+            <p>{{. | markupPipeWords | newlinesToBR | markupFirstWord}}</p>
           {{end}}
           <pre>{{.Decl}}</pre>
           </div>
diff --git a/src/util/fetch_ech_config_list.go b/src/util/fetch_ech_config_list.go
index badaae2..03b2f87 100644
--- a/src/util/fetch_ech_config_list.go
+++ b/src/util/fetch_ech_config_list.go
@@ -175,7 +175,7 @@
 		// Verify that this response answers the question that we asked in the
 		// query. If the resolver encountered any CNAMEs, it's not guaranteed
 		// that the response will contain a question with the same QNAME as our
-		// query. However, RFC 8499 Section 4 indicates that in general use, the
+		// query. However, RFC8499 Section 4 indicates that in general use, the
 		// response's QNAME should match the query, so we will make that
 		// assumption.
 		q, err := p.Question()
diff --git a/src/util/fipstools/acvp/ACVP.md b/src/util/fipstools/acvp/ACVP.md
index ba5bdbb..c9570c6 100644
--- a/src/util/fipstools/acvp/ACVP.md
+++ b/src/util/fipstools/acvp/ACVP.md
@@ -49,8 +49,6 @@
 | 3DES/encrypt         | Key, input block, num iterations¹ | Result, Previous result |
 | AES-CBC/decrypt      | Key, ciphertext, IV, num iterations¹ | Result, Previous result |
 | AES-CBC/encrypt      | Key, plaintext, IV, num iterations¹ | Result, Previous result |
-| AES-CBC-CS3/decrypt  | Key, ciphertext, IV, num iterations² | Result |
-| AES-CBC-CS3/encrypt  | Key, plaintext, IV, num iterations²  | Result |
 | AES-CCM/open         | Tag length, key, ciphertext, nonce, ad | One-byte success flag, plaintext or empty |
 | AES-CCM/seal         | Tag length, key, plaintext, nonce, ad | Ciphertext |
 | AES-CTR/decrypt      | Key, ciphertext, initial counter, constant 1 | Plaintext |
@@ -68,8 +66,6 @@
 | CMAC-AES             | Number output bytes, key, message | MAC |
 | CMAC-AES/verify      | Key, message, claimed MAC | One-byte success flag |
 | ctrDRBG/AES-256      | Output length, entropy, personalisation, ad1, ad2, nonce | Output |
-| ctrDRBG-reseed/AES-256| Output length, entropy, personalisation, reseedAD, reseedEntropy, ad1, ad2, nonce | Output |
-| ctrDRBG-pr/AES-256   | Output length, entropy, personalisation, ad1, entropy1, ad2, entropy2, nonce | Output |
 | ECDH/&lt;CURVE&gt;   | X, Y, private key | X, Y, shared key |
 | ECDSA/keyGen         | Curve name | Private key, X, Y |
 | ECDSA/keyVer         | Curve name, X, Y | Single-byte valid flag |
@@ -83,9 +79,6 @@
 | HMAC-SHA2-384        | Value to hash, key        | Digest  |
 | HMAC-SHA2-512        | Value to hash, key        | Digest  |
 | HMAC-SHA2-512/256    | Value to hash, key        | Digest  |
-| hmacDRBG/&lt;HASH&gt;| Output length, entropy, personalisation, ad1, ad2, nonce | Output |
-| hmacDRBG-reseed/&lt;HASH&gt;| Output length, entropy, personalisation, reseedAD, reseedEntropy, ad1, ad2, nonce | Output |
-| hmacDRBG-pr/&lt;HASH&gt;| Output length, entropy, personalisation, ad1, entropy1, ad2, entropy2, nonce | Output |
 | KDF-counter          | Number output bytes, PRF name, counter location string, key, number of counter bits | Counter, output |
 | RSA/keyGen           | Modulus bit-size | e, p, q, n, d |
 | RSA/sigGen/&lt;HASH&gt;/pkcs1v1.5 | Modulus bit-size | n, e, signature |
@@ -108,8 +101,6 @@
 
 ¹ The iterated tests would result in excessive numbers of round trips if the module wrapper handled only basic operations. Thus some ACVP logic is pushed down for these tests so that the inner loop can be handled locally. Either read the NIST documentation ([block-ciphers](https://pages.nist.gov/ACVP/draft-celi-acvp-symmetric.html#name-monte-carlo-tests-for-block) [hashes](https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#name-monte-carlo-tests-for-sha-1)) to understand the iteration count and return values or, probably more fruitfully, see how these functions are handled in the `modulewrapper` directory.
 
-² Will always be one because MCT tests are not supported for CS3.
-
 ## Online operation
 
 If you have credentials to speak to either of the NIST ACVP servers then you can run the tool in online mode.
diff --git a/src/util/fipstools/acvp/acvptool/acvp.go b/src/util/fipstools/acvp/acvptool/acvp.go
index f477d65..f497532 100644
--- a/src/util/fipstools/acvp/acvptool/acvp.go
+++ b/src/util/fipstools/acvp/acvptool/acvp.go
@@ -178,20 +178,18 @@
 	return s
 }
 
-// looksLikeHeaderElement returns true iff element looks like it's a header, not
-// a test. Some ACVP files contain a header as the first element that should be
-// duplicated into the response, and some don't. If the element contains
-// a "url" field, or if it's missing an "algorithm" field, then we guess that
-// it's a header.
+// looksLikeHeaderElement returns true iff element looks like it's a header,
+// not a test. Some ACVP files contain a header as the first element that
+// should be duplicated into the response, and some don't. If the element
+// contains a "url" field then we guess that it's a header.
 func looksLikeHeaderElement(element json.RawMessage) bool {
 	var headerFields struct {
 		URL string `json:"url"`
-		Algorithm string `json:"algorithm"`
 	}
 	if err := json.Unmarshal(element, &headerFields); err != nil {
 		return false
 	}
-	return len(headerFields.URL) > 0 || len(headerFields.Algorithm) == 0
+	return len(headerFields.URL) > 0
 }
 
 // processFile reads a file containing vector sets, at least in the format
diff --git a/src/util/fipstools/acvp/acvptool/subprocess/drbg.go b/src/util/fipstools/acvp/acvptool/subprocess/drbg.go
index d2f7572..b9a1cb8 100644
--- a/src/util/fipstools/acvp/acvptool/subprocess/drbg.go
+++ b/src/util/fipstools/acvp/acvptool/subprocess/drbg.go
@@ -40,20 +40,18 @@
 	AdditionalDataBits    uint64 `json:"additionalInputLen"`
 	RetBits               uint64 `json:"returnedBitsLen"`
 	Tests                 []struct {
-		ID                 uint64           `json:"tcId"`
-		EntropyHex         string           `json:"entropyInput"`
-		NonceHex           string           `json:"nonce"`
-		PersonalizationHex string           `json:"persoString"`
-		Other              []drbgOtherInput `json:"otherInput"`
+		ID                 uint64 `json:"tcId"`
+		EntropyHex         string `json:"entropyInput"`
+		NonceHex           string `json:"nonce"`
+		PersonalizationHex string `json:"persoString"`
+		Other              []struct {
+			AdditionalDataHex string `json:"additionalInput"`
+			EntropyHex        string `json:"entropyInput"`
+			Use               string `json:"intendedUse"`
+		} `json:"otherInput"`
 	} `json:"tests"`
 }
 
-type drbgOtherInput struct {
-	Use               string `json:"intendedUse"`
-	AdditionalDataHex string `json:"additionalInput"`
-	EntropyHex        string `json:"entropyInput"`
-}
-
 type drbgTestGroupResponse struct {
 	ID    uint64             `json:"tgId"`
 	Tests []drbgTestResponse `json:"tests"`
@@ -92,6 +90,14 @@
 			return nil, fmt.Errorf("test group %d specifies mode %q, which is not supported for the %s algorithm", group.ID, group.Mode, d.algo)
 		}
 
+		if group.PredictionResistance {
+			return nil, fmt.Errorf("Test group %d specifies prediction-resistance mode, which is not supported", group.ID)
+		}
+
+		if group.Reseed {
+			return nil, fmt.Errorf("Test group %d requests re-seeding, which is not supported", group.ID)
+		}
+
 		if group.RetBits%8 != 0 {
 			return nil, fmt.Errorf("Test group %d requests %d-bit outputs, but fractional-bytes are not supported", group.ID, group.RetBits)
 		}
@@ -112,38 +118,26 @@
 				return nil, fmt.Errorf("failed to extract personalization hex from test case %d/%d: %s", group.ID, test.ID, err)
 			}
 
+			const numAdditionalInputs = 2
+			if len(test.Other) != numAdditionalInputs {
+				return nil, fmt.Errorf("test case %d/%d provides %d additional inputs, but subprocess only expects %d", group.ID, test.ID, len(test.Other), numAdditionalInputs)
+			}
+
+			var additionalInputs [numAdditionalInputs][]byte
+			for i, other := range test.Other {
+				if other.Use != "generate" {
+					return nil, fmt.Errorf("other %d from test case %d/%d has use %q, but expected 'generate'", i, group.ID, test.ID, other.Use)
+				}
+				additionalInputs[i], err = extractField(other.AdditionalDataHex, group.AdditionalDataBits)
+				if err != nil {
+					return nil, fmt.Errorf("failed to extract additional input %d from test case %d/%d: %s", i, group.ID, test.ID, err)
+				}
+			}
+
 			outLen := group.RetBits / 8
 			var outLenBytes [4]byte
 			binary.LittleEndian.PutUint32(outLenBytes[:], uint32(outLen))
-
-			var result [][]byte
-			if group.PredictionResistance {
-				var a1, a2, a3, a4 []byte
-				if err := extractOtherInputs(test.Other, []drbgOtherInputExpectations{
-					{"generate", group.AdditionalDataBits, &a1, group.EntropyBits, &a2},
-					{"generate", group.AdditionalDataBits, &a3, group.EntropyBits, &a4}}); err != nil {
-					return nil, fmt.Errorf("failed to parse other inputs from test case %d/%d: %s", group.ID, test.ID, err)
-				}
-				result, err = m.Transact(d.algo+"-pr/"+group.Mode, 1, outLenBytes[:], ent, perso, a1, a2, a3, a4, nonce)
-			} else if group.Reseed {
-				var a1, a2, a3, a4 []byte
-				if err := extractOtherInputs(test.Other, []drbgOtherInputExpectations{
-					{"reSeed", group.AdditionalDataBits, &a1, group.EntropyBits, &a2},
-					{"generate", group.AdditionalDataBits, &a3, 0, nil},
-					{"generate", group.AdditionalDataBits, &a4, 0, nil}}); err != nil {
-					return nil, fmt.Errorf("failed to parse other inputs from test case %d/%d: %s", group.ID, test.ID, err)
-				}
-				result, err = m.Transact(d.algo+"-reseed/"+group.Mode, 1, outLenBytes[:], ent, perso, a1, a2, a3, a4, nonce)
-			} else {
-				var a1, a2 []byte
-				if err := extractOtherInputs(test.Other, []drbgOtherInputExpectations{
-					{"generate", group.AdditionalDataBits, &a1, 0, nil},
-					{"generate", group.AdditionalDataBits, &a2, 0, nil}}); err != nil {
-					return nil, fmt.Errorf("failed to parse other inputs from test case %d/%d: %s", group.ID, test.ID, err)
-				}
-				result, err = m.Transact(d.algo+"/"+group.Mode, 1, outLenBytes[:], ent, perso, a1, a2, nonce)
-			}
-
+			result, err := m.Transact(d.algo+"/"+group.Mode, 1, outLenBytes[:], ent, perso, additionalInputs[0], additionalInputs[1], nonce)
 			if err != nil {
 				return nil, fmt.Errorf("DRBG operation failed: %s", err)
 			}
@@ -165,52 +159,6 @@
 	return ret, nil
 }
 
-type drbgOtherInputExpectations struct {
-	use                   string
-	additionalInputBitLen uint64
-	additionalInputOut    *[]byte
-	entropyBitLen         uint64
-	entropyOut            *[]byte
-}
-
-func extractOtherInputs(inputs []drbgOtherInput, expected []drbgOtherInputExpectations) (err error) {
-	if len(inputs) != len(expected) {
-		return fmt.Errorf("found %d other inputs but %d were expected", len(inputs), len(expected))
-	}
-
-	for i := range inputs {
-		input, expect := &inputs[i], &expected[i]
-
-		if input.Use != expect.use {
-			return fmt.Errorf("other input #%d has type %q but expected %q", i, input.Use, expect.use)
-		}
-
-		if expect.additionalInputBitLen == 0 {
-			if len(input.AdditionalDataHex) != 0 {
-				return fmt.Errorf("other input #%d has unexpected additional input", i)
-			}
-		} else {
-			*expect.additionalInputOut, err = extractField(input.AdditionalDataHex, expect.additionalInputBitLen)
-			if err != nil {
-				return err
-			}
-		}
-
-		if expect.entropyBitLen == 0 {
-			if len(input.EntropyHex) != 0 {
-				return fmt.Errorf("other input #%d has unexpected entropy value", i)
-			}
-		} else {
-			*expect.entropyOut, err = extractField(input.EntropyHex, expect.entropyBitLen)
-			if err != nil {
-				return err
-			}
-		}
-	}
-
-	return nil
-}
-
 // validate the length and hex of a JSON field in test vectors
 func extractField(fieldHex string, bits uint64) ([]byte, error) {
 	if uint64(len(fieldHex))*4 != bits {
diff --git a/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go b/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go
index 844c9c4..fe74993 100644
--- a/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go
+++ b/src/util/fipstools/acvp/acvptool/subprocess/subprocess.go
@@ -71,38 +71,37 @@
 	}
 
 	m.primitives = map[string]primitive{
-		"SHA-1":            &hashPrimitive{"SHA-1", 20},
-		"SHA2-224":         &hashPrimitive{"SHA2-224", 28},
-		"SHA2-256":         &hashPrimitive{"SHA2-256", 32},
-		"SHA2-384":         &hashPrimitive{"SHA2-384", 48},
-		"SHA2-512":         &hashPrimitive{"SHA2-512", 64},
-		"SHA2-512/256":     &hashPrimitive{"SHA2-512/256", 32},
-		"ACVP-AES-ECB":     &blockCipher{"AES", 16, 2, true, false, iterateAES},
-		"ACVP-AES-CBC":     &blockCipher{"AES-CBC", 16, 2, true, true, iterateAESCBC},
-		"ACVP-AES-CBC-CS3": &blockCipher{"AES-CBC-CS3", 16, 1, false, true, iterateAESCBC},
-		"ACVP-AES-CTR":     &blockCipher{"AES-CTR", 16, 1, false, true, nil},
-		"ACVP-AES-XTS":     &xts{},
-		"ACVP-TDES-ECB":    &blockCipher{"3DES-ECB", 8, 3, true, false, iterate3DES},
-		"ACVP-TDES-CBC":    &blockCipher{"3DES-CBC", 8, 3, true, true, iterate3DESCBC},
-		"ACVP-AES-GCM":     &aead{"AES-GCM", false},
-		"ACVP-AES-GMAC":    &aead{"AES-GCM", false},
-		"ACVP-AES-CCM":     &aead{"AES-CCM", true},
-		"ACVP-AES-KW":      &aead{"AES-KW", false},
-		"ACVP-AES-KWP":     &aead{"AES-KWP", false},
-		"HMAC-SHA-1":       &hmacPrimitive{"HMAC-SHA-1", 20},
-		"HMAC-SHA2-224":    &hmacPrimitive{"HMAC-SHA2-224", 28},
-		"HMAC-SHA2-256":    &hmacPrimitive{"HMAC-SHA2-256", 32},
-		"HMAC-SHA2-384":    &hmacPrimitive{"HMAC-SHA2-384", 48},
-		"HMAC-SHA2-512":    &hmacPrimitive{"HMAC-SHA2-512", 64},
-		"ctrDRBG":          &drbg{"ctrDRBG", map[string]bool{"AES-128": true, "AES-192": true, "AES-256": true}},
-		"hmacDRBG":         &drbg{"hmacDRBG", map[string]bool{"SHA-1": true, "SHA2-224": true, "SHA2-256": true, "SHA2-384": true, "SHA2-512": true}},
-		"KDF":              &kdfPrimitive{},
-		"KAS-KDF":          &hkdf{},
-		"CMAC-AES":         &keyedMACPrimitive{"CMAC-AES"},
-		"RSA":              &rsa{},
-		"kdf-components":   &tlsKDF{},
-		"KAS-ECC-SSC":      &kas{},
-		"KAS-FFC-SSC":      &kasDH{},
+		"SHA-1":          &hashPrimitive{"SHA-1", 20},
+		"SHA2-224":       &hashPrimitive{"SHA2-224", 28},
+		"SHA2-256":       &hashPrimitive{"SHA2-256", 32},
+		"SHA2-384":       &hashPrimitive{"SHA2-384", 48},
+		"SHA2-512":       &hashPrimitive{"SHA2-512", 64},
+		"SHA2-512/256":   &hashPrimitive{"SHA2-512/256", 32},
+		"ACVP-AES-ECB":   &blockCipher{"AES", 16, 2, true, false, iterateAES},
+		"ACVP-AES-CBC":   &blockCipher{"AES-CBC", 16, 2, true, true, iterateAESCBC},
+		"ACVP-AES-CTR":   &blockCipher{"AES-CTR", 16, 1, false, true, nil},
+		"ACVP-AES-XTS":   &xts{},
+		"ACVP-TDES-ECB":  &blockCipher{"3DES-ECB", 8, 3, true, false, iterate3DES},
+		"ACVP-TDES-CBC":  &blockCipher{"3DES-CBC", 8, 3, true, true, iterate3DESCBC},
+		"ACVP-AES-GCM":   &aead{"AES-GCM", false},
+		"ACVP-AES-GMAC":  &aead{"AES-GCM", false},
+		"ACVP-AES-CCM":   &aead{"AES-CCM", true},
+		"ACVP-AES-KW":    &aead{"AES-KW", false},
+		"ACVP-AES-KWP":   &aead{"AES-KWP", false},
+		"HMAC-SHA-1":     &hmacPrimitive{"HMAC-SHA-1", 20},
+		"HMAC-SHA2-224":  &hmacPrimitive{"HMAC-SHA2-224", 28},
+		"HMAC-SHA2-256":  &hmacPrimitive{"HMAC-SHA2-256", 32},
+		"HMAC-SHA2-384":  &hmacPrimitive{"HMAC-SHA2-384", 48},
+		"HMAC-SHA2-512":  &hmacPrimitive{"HMAC-SHA2-512", 64},
+		"ctrDRBG":        &drbg{"ctrDRBG", map[string]bool{"AES-128": true, "AES-192": true, "AES-256": true}},
+		"hmacDRBG":       &drbg{"hmacDRBG", map[string]bool{"SHA-1": true, "SHA2-224": true, "SHA2-256": true, "SHA2-384": true, "SHA2-512": true}},
+		"KDF":            &kdfPrimitive{},
+		"KAS-KDF":        &hkdf{},
+		"CMAC-AES":       &keyedMACPrimitive{"CMAC-AES"},
+		"RSA":            &rsa{},
+		"kdf-components": &tlsKDF{},
+		"KAS-ECC-SSC":    &kas{},
+		"KAS-FFC-SSC":    &kasDH{},
 	}
 	m.primitives["ECDSA"] = &ecdsa{"ECDSA", map[string]bool{"P-224": true, "P-256": true, "P-384": true, "P-521": true}, m.primitives}
 
diff --git a/src/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-CBC-CS3.bz2 b/src/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-CBC-CS3.bz2
deleted file mode 100644
index 9a6c4c0..0000000
--- a/src/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-CBC-CS3.bz2
+++ /dev/null
Binary files differ
diff --git a/src/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-GCM.bz2 b/src/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-GCM.bz2
index d50948d..df90c77 100644
--- a/src/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-GCM.bz2
+++ b/src/util/fipstools/acvp/acvptool/test/expected/ACVP-AES-GCM.bz2
Binary files differ
diff --git a/src/util/fipstools/acvp/acvptool/test/expected/hmacDRBG.bz2 b/src/util/fipstools/acvp/acvptool/test/expected/hmacDRBG.bz2
deleted file mode 100644
index 286ba0c..0000000
--- a/src/util/fipstools/acvp/acvptool/test/expected/hmacDRBG.bz2
+++ /dev/null
Binary files differ
diff --git a/src/util/fipstools/acvp/acvptool/test/tests.json b/src/util/fipstools/acvp/acvptool/test/tests.json
index f613917..dfbaac5 100644
--- a/src/util/fipstools/acvp/acvptool/test/tests.json
+++ b/src/util/fipstools/acvp/acvptool/test/tests.json
@@ -1,6 +1,5 @@
 [
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-CBC.bz2", "Out": "expected/ACVP-AES-CBC.bz2"},
-{"Wrapper": "testmodulewrapper", "In": "vectors/ACVP-AES-CBC-CS3.bz2", "Out": "expected/ACVP-AES-CBC-CS3.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-CCM.bz2", "Out": "expected/ACVP-AES-CCM.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-CTR.bz2", "Out": "expected/ACVP-AES-CTR.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-ECB.bz2", "Out": "expected/ACVP-AES-ECB.bz2"},
@@ -19,7 +18,6 @@
 {"Wrapper": "modulewrapper", "In": "vectors/HMAC-SHA2-256.bz2", "Out": "expected/HMAC-SHA2-256.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/HMAC-SHA2-384.bz2", "Out": "expected/HMAC-SHA2-384.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/HMAC-SHA2-512.bz2", "Out": "expected/HMAC-SHA2-512.bz2"},
-{"Wrapper": "testmodulewrapper", "In": "vectors/hmacDRBG.bz2", "Out": "expected/hmacDRBG.bz2"},
 {"Wrapper": "testmodulewrapper", "In": "vectors/KAS-KDF.bz2", "Out": "expected/KAS-KDF.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/KAS-ECC-SSC.bz2"},
 {"Wrapper": "modulewrapper", "In": "vectors/KAS-FFC-SSC.bz2"},
diff --git a/src/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-CBC-CS3.bz2 b/src/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-CBC-CS3.bz2
deleted file mode 100644
index a46186d..0000000
--- a/src/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-CBC-CS3.bz2
+++ /dev/null
Binary files differ
diff --git a/src/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-GCM.bz2 b/src/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-GCM.bz2
index 719bcc0..1d49d05 100644
--- a/src/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-GCM.bz2
+++ b/src/util/fipstools/acvp/acvptool/test/vectors/ACVP-AES-GCM.bz2
Binary files differ
diff --git a/src/util/fipstools/acvp/acvptool/test/vectors/hmacDRBG.bz2 b/src/util/fipstools/acvp/acvptool/test/vectors/hmacDRBG.bz2
deleted file mode 100644
index 6bfaa4e..0000000
--- a/src/util/fipstools/acvp/acvptool/test/vectors/hmacDRBG.bz2
+++ /dev/null
Binary files differ
diff --git a/src/util/fipstools/acvp/acvptool/testmodulewrapper/cts_test.go b/src/util/fipstools/acvp/acvptool/testmodulewrapper/cts_test.go
deleted file mode 100644
index 5e7a597..0000000
--- a/src/util/fipstools/acvp/acvptool/testmodulewrapper/cts_test.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2021, Google Inc.
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-package main
-
-import (
-	"bytes"
-	"crypto/aes"
-	"crypto/rand"
-	"encoding/hex"
-	"testing"
-)
-
-func TestCTSRoundTrip(t *testing.T) {
-	var buf [aes.BlockSize * 8]byte
-	var key, iv [16]byte
-	rand.Reader.Read(buf[:])
-	rand.Reader.Read(key[:])
-	rand.Reader.Read(iv[:])
-
-	for i := aes.BlockSize; i < len(buf); i++ {
-		in := buf[:i]
-		ciphertext := doCTSEncrypt(key[:], in[:], iv[:])
-		if len(ciphertext) != len(in) {
-			t.Errorf("incorrect ciphertext length for input length %d", len(in))
-			continue
-		}
-		out := doCTSDecrypt(key[:], ciphertext, iv[:])
-
-		if !bytes.Equal(in[:], out) {
-			t.Errorf("did not round trip for length %d", len(in))
-		}
-	}
-}
-
-func TestCTSVectors(t *testing.T) {
-	tests := []struct {
-		plaintextHex  string
-		ciphertextHex string
-		ivHex         string
-	}{
-		// Test vectors from OpenSSL.
-		{
-			"4920776f756c64206c696b652074686520",
-			"c6353568f2bf8cb4d8a580362da7ff7f97",
-			"00000000000000000000000000000000",
-		},
-		{
-			"4920776f756c64206c696b65207468652047656e6572616c20476175277320",
-			"fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5",
-			"00000000000000000000000000000000",
-		},
-		{
-			"4920776f756c64206c696b65207468652047656e6572616c2047617527732043",
-			"39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584",
-			"00000000000000000000000000000000",
-		},
-		{
-			"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c",
-			"97687268d6ecccc0c07b25e25ecfe584b3fffd940c16a18c1b5549d2f838029e39312523a78662d5be7fcbcc98ebf5",
-			"00000000000000000000000000000000",
-		},
-		{
-			"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c",
-			"5432a630742dee7beb70f9f1400ee6a0426da5c54a9990f5ae0b7825f51f0060b557cfb581949a4bdf3bb67dedd472",
-			"000102030405060708090a0b0c0d0e0f",
-		},
-	}
-
-	key := fromHex("636869636b656e207465726979616b69")
-
-	for i, test := range tests {
-		plaintext := fromHex(test.plaintextHex)
-		iv := fromHex(test.ivHex)
-		ciphertext := doCTSEncrypt(key, plaintext, iv)
-		if got := hex.EncodeToString(ciphertext); got != test.ciphertextHex {
-			t.Errorf("#%d: unexpected ciphertext %s, want %s", i, got, test.ciphertextHex)
-		}
-		plaintextAgain := doCTSDecrypt(key, ciphertext, iv)
-		if !bytes.Equal(plaintext, plaintextAgain) {
-			t.Errorf("#%d: did not round trip", i)
-		}
-	}
-}
diff --git a/src/util/fipstools/acvp/acvptool/testmodulewrapper/hmac_drbg.go b/src/util/fipstools/acvp/acvptool/testmodulewrapper/hmac_drbg.go
deleted file mode 100644
index 46fae69..0000000
--- a/src/util/fipstools/acvp/acvptool/testmodulewrapper/hmac_drbg.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2021, Google Inc.
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-package main
-
-import (
-	"crypto/hmac"
-	"crypto/sha256"
-)
-
-// See SP 800-90Ar1, section 10.1.2
-type HMACDRBGSHA256 struct {
-	k, v [32]byte
-}
-
-func NewHMACDRBG(entropy, nonce, personalisation []byte) *HMACDRBGSHA256 {
-	ret := new(HMACDRBGSHA256)
-	ret.init(entropy, nonce, personalisation)
-	return ret
-}
-
-func (drbg *HMACDRBGSHA256) init(entropy, nonce, personalisation []byte) {
-	for i := range drbg.k {
-		drbg.k[i] = 0
-	}
-	for i := range drbg.v {
-		drbg.v[i] = 1
-	}
-
-	seed := make([]byte, 0, len(entropy)+len(nonce)+len(personalisation))
-	seed = append(seed, entropy...)
-	seed = append(seed, nonce...)
-	seed = append(seed, personalisation...)
-	drbg.update(seed)
-}
-
-func (drbg *HMACDRBGSHA256) update(data []byte) {
-	buf := make([]byte, 0, len(drbg.v)+1+len(data))
-	buf = append(buf, drbg.v[:]...)
-	buf = append(buf, 0)
-	buf = append(buf, data...)
-
-	mac := hmac.New(sha256.New, drbg.k[:])
-	mac.Write(buf)
-	mac.Sum(drbg.k[:0])
-
-	mac = hmac.New(sha256.New, drbg.k[:])
-	mac.Write(drbg.v[:])
-	mac.Sum(drbg.v[:0])
-
-	if len(data) > 0 {
-		copy(buf, drbg.v[:])
-		buf[len(drbg.v)] = 1
-
-		mac = hmac.New(sha256.New, drbg.k[:])
-		mac.Write(buf)
-		mac.Sum(drbg.k[:0])
-
-		mac = hmac.New(sha256.New, drbg.k[:])
-		mac.Write(drbg.v[:])
-		mac.Sum(drbg.v[:0])
-	}
-}
-
-func (drbg *HMACDRBGSHA256) Reseed(entropy, additionalInput []byte) {
-	buf := make([]byte, 0, len(entropy)+len(additionalInput))
-	buf = append(buf, entropy...)
-	buf = append(buf, additionalInput...)
-	drbg.update(buf)
-}
-
-func (drbg *HMACDRBGSHA256) Generate(out []byte, additionalInput []byte) {
-	if len(additionalInput) > 0 {
-		drbg.update(additionalInput)
-	}
-
-	done := 0
-	for done < len(out) {
-		mac := hmac.New(sha256.New, drbg.k[:])
-		mac.Write(drbg.v[:])
-		mac.Sum(drbg.v[:0])
-
-		done += copy(out[done:], drbg.v[:])
-	}
-
-	drbg.update(additionalInput)
-}
diff --git a/src/util/fipstools/acvp/acvptool/testmodulewrapper/hmac_drbg_test.go b/src/util/fipstools/acvp/acvptool/testmodulewrapper/hmac_drbg_test.go
deleted file mode 100644
index ca6304d..0000000
--- a/src/util/fipstools/acvp/acvptool/testmodulewrapper/hmac_drbg_test.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) 2021, Google Inc.
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-package main
-
-import (
-	"encoding/hex"
-	"testing"
-)
-
-/*
-[SHA-256]
-[PredictionResistance = False]
-[EntropyInputLen = 256]
-[NonceLen = 128]
-[PersonalizationStringLen = 0]
-[AdditionalInputLen = 0]
-[ReturnedBitsLen = 1024]
-
-COUNT = 0
-EntropyInput = 06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d
-Nonce = 0e66f71edc43e42a45ad3c6fc6cdc4df
-PersonalizationString =
-** INSTANTIATE:
-	V   = 81e0d8830ed2d16f9b288a1cb289c5fab3f3c5c28131be7cafedcc7734604d34
-	Key = 17dc11c2389f5eeb9d0f6a5148a1ea83ee8a828f4f140ac78272a0da435fa121
-EntropyInputReseed = 01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552
-AdditionalInputReseed =
-** RESEED:
-	V   = c246fa97570ba2b9d9e5b453fe4632366f146fbd8491146563eb463c9eafe50c
-	Key = ca43e73325de43c41d7e0a7a3163fb04061b09fcee4c7b8884e969e3bdfdff9a
-AdditionalInput =
-** GENERATE (FIRST CALL):
-	V   = df67d0816d6a8f3b73ba7638ea113bef0e33a1da451272ef1472211fb31c1cd6
-	Key = 8be4c7f9f249d5af2c6345a8f07af14be1d7adc2b9892286ffe37760d8aa5a1b
-AdditionalInput =
-ReturnedBits = 76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9ec3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6a38cc5b6499dc43f7f2bd09e1e0f8f5885935124
-** GENERATE (SECOND CALL):
-	V   = 80524881711e89a61e6fe7169581e50fb9ad642f3dff48fba5773352fa04cec3
-	Key = 5ed31bc06cc4f3a97f7f34929b0558b0c34de1f4bd1cef456a8364140e2d9f41
-*/
-
-func TestHMACDRBG(t *testing.T) {
-	drbg := NewHMACDRBG(fromHex("06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"),
-		fromHex("0e66f71edc43e42a45ad3c6fc6cdc4df"),
-		nil)
-
-	drbg.Reseed(fromHex("01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"), nil)
-
-	var out [1024 / 8]byte
-	drbg.Generate(out[:], nil)
-	drbg.Generate(out[:], nil)
-
-	if hex.EncodeToString(out[:]) != "76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9ec3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6a38cc5b6499dc43f7f2bd09e1e0f8f5885935124" {
-		t.Errorf("Incorrect result: %x", out)
-	}
-}
-
-/*
-EntropyInput = 6c1f4bffc476e488fb57eb80dc106cf2b417bad22b196baa6346958256db490f
-Nonce = 5f1b92223e3909e43677da2f588a6d19
-PersonalizationString =
-AdditionalInput = e6cd940610375e504fa80406120b34d498b022393436e910c0ba2560603fd066
-EntropyInputPR = abaca65695bd5d289880453850fc8289b76f78b43f970ed32f4125a941165515
-AdditionalInput = d20082c5bdf6f6711af391e7d01046b9d3610827de63aa2671a5f5ad07b90841
-EntropyInputPR = 4a39b666cf861816d7d82ef6e23f70f149d74d9bd499eea19b622e751c43d839
-ReturnedBits = d3c36e4ae25ff21a95a157a89f13eb976362a695ea755f0465ed4a7bb20c5cb3
-*/
-
-func TestHMACDRBGPredictionResistance(t *testing.T) {
-	drbg := NewHMACDRBG(fromHex("6c1f4bffc476e488fb57eb80dc106cf2b417bad22b196baa6346958256db490f"),
-		fromHex("5f1b92223e3909e43677da2f588a6d19"),
-		nil)
-
-	var out [32]byte
-	drbg.Reseed(fromHex("abaca65695bd5d289880453850fc8289b76f78b43f970ed32f4125a941165515"), fromHex("e6cd940610375e504fa80406120b34d498b022393436e910c0ba2560603fd066"))
-	drbg.Generate(out[:], nil)
-	drbg.Reseed(fromHex("4a39b666cf861816d7d82ef6e23f70f149d74d9bd499eea19b622e751c43d839"), fromHex("d20082c5bdf6f6711af391e7d01046b9d3610827de63aa2671a5f5ad07b90841"))
-	drbg.Generate(out[:], nil)
-
-	if hex.EncodeToString(out[:]) != "d3c36e4ae25ff21a95a157a89f13eb976362a695ea755f0465ed4a7bb20c5cb3" {
-		t.Errorf("Incorrect result: %x", out)
-	}
-}
-
-func fromHex(h string) []byte {
-	ret, err := hex.DecodeString(h)
-	if err != nil {
-		panic(err)
-	}
-	return ret
-}
diff --git a/src/util/fipstools/acvp/acvptool/testmodulewrapper/testmodulewrapper.go b/src/util/fipstools/acvp/acvptool/testmodulewrapper/testmodulewrapper.go
index afb1804..e989461 100644
--- a/src/util/fipstools/acvp/acvptool/testmodulewrapper/testmodulewrapper.go
+++ b/src/util/fipstools/acvp/acvptool/testmodulewrapper/testmodulewrapper.go
@@ -1,17 +1,3 @@
-// Copyright (c) 2021, Google Inc.
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
 // testmodulewrapper is a modulewrapper binary that works with acvptool and
 // implements the primitives that BoringSSL's modulewrapper doesn't, so that
 // we have something that can exercise all the code in avcptool.
@@ -21,7 +7,6 @@
 import (
 	"bytes"
 	"crypto/aes"
-	"crypto/cipher"
 	"crypto/hmac"
 	"crypto/rand"
 	"crypto/sha256"
@@ -36,15 +21,11 @@
 )
 
 var handlers = map[string]func([][]byte) error{
-	"getConfig":                getConfig,
-	"KDF-counter":              kdfCounter,
-	"AES-XTS/encrypt":          xtsEncrypt,
-	"AES-XTS/decrypt":          xtsDecrypt,
-	"HKDF/SHA2-256":            hkdfMAC,
-	"hmacDRBG-reseed/SHA2-256": hmacDRBGReseed,
-	"hmacDRBG-pr/SHA2-256":     hmacDRBGPredictionResistance,
-	"AES-CBC-CS3/encrypt":      ctsEncrypt,
-	"AES-CBC-CS3/decrypt":      ctsDecrypt,
+	"getConfig":       getConfig,
+	"KDF-counter":     kdfCounter,
+	"AES-XTS/encrypt": xtsEncrypt,
+	"AES-XTS/decrypt": xtsDecrypt,
+	"HKDF/SHA2-256":   hkdfMAC,
 }
 
 func getConfig(args [][]byte) error {
@@ -123,44 +104,6 @@
 		}],
 		"l": 256,
 		"z": [256, 384]
-	}, {
-		"algorithm": "hmacDRBG",
-		"revision": "1.0",
-		"predResistanceEnabled": [false, true],
-		"reseedImplemented": true,
-		"capabilities": [{
-			"mode": "SHA2-256",
-			"derFuncEnabled": false,
-			"entropyInputLen": [
-				256
-			],
-			"nonceLen": [
-				128
-			],
-			"persoStringLen": [
-				256
-			],
-			"additionalInputLen": [
-				256
-			],
-			"returnedBitsLen": 256
-		}]
-	}, {
-		"algorithm": "ACVP-AES-CBC-CS3",
-		"revision": "1.0",
-		"payloadLen": [{
-			"min": 128,
-			"max": 2048,
-			"increment": 8
-		}],
-		"direction": [
-		  "encrypt",
-		  "decrypt"
-		],
-		"keyLen": [
-		  128,
-		  256
-		]
 	}
 ]`))
 }
@@ -303,167 +246,8 @@
 	return reply(ret)
 }
 
-func hmacDRBGReseed(args [][]byte) error {
-	if len(args) != 8 {
-		return fmt.Errorf("hmacDRBG received %d args, wanted 8", len(args))
-	}
-
-	outLenBytes, entropy, personalisation, reseedAdditionalData, reseedEntropy, additionalData1, additionalData2, nonce := args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]
-
-	if len(outLenBytes) != 4 {
-		return fmt.Errorf("uint32 length was %d bytes long", len(outLenBytes))
-	}
-	outLen := binary.LittleEndian.Uint32(outLenBytes)
-	out := make([]byte, outLen)
-
-	drbg := NewHMACDRBG(entropy, nonce, personalisation)
-	drbg.Reseed(reseedEntropy, reseedAdditionalData)
-	drbg.Generate(out, additionalData1)
-	drbg.Generate(out, additionalData2)
-
-	return reply(out)
-}
-
-func hmacDRBGPredictionResistance(args [][]byte) error {
-	if len(args) != 8 {
-		return fmt.Errorf("hmacDRBG received %d args, wanted 8", len(args))
-	}
-
-	outLenBytes, entropy, personalisation, additionalData1, entropy1, additionalData2, entropy2, nonce := args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]
-
-	if len(outLenBytes) != 4 {
-		return fmt.Errorf("uint32 length was %d bytes long", len(outLenBytes))
-	}
-	outLen := binary.LittleEndian.Uint32(outLenBytes)
-	out := make([]byte, outLen)
-
-	drbg := NewHMACDRBG(entropy, nonce, personalisation)
-	drbg.Reseed(entropy1, additionalData1)
-	drbg.Generate(out, nil)
-	drbg.Reseed(entropy2, additionalData2)
-	drbg.Generate(out, nil)
-
-	return reply(out)
-}
-
-func swapFinalTwoAESBlocks(d []byte) {
-	var blockNMinus1 [aes.BlockSize]byte
-	copy(blockNMinus1[:], d[len(d)-2*aes.BlockSize:])
-	copy(d[len(d)-2*aes.BlockSize:], d[len(d)-aes.BlockSize:])
-	copy(d[len(d)-aes.BlockSize:], blockNMinus1[:])
-}
-
-func roundUp(n, m int) int {
-	return n + (m-(n%m))%m
-}
-
-func doCTSEncrypt(key, origPlaintext, iv []byte) []byte {
-	// https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38a-add.pdf
-	if len(origPlaintext) < aes.BlockSize {
-		panic("input too small")
-	}
-
-	plaintext := make([]byte, roundUp(len(origPlaintext), aes.BlockSize))
-	copy(plaintext, origPlaintext)
-
-	block, err := aes.NewCipher(key)
-	if err != nil {
-		panic(err)
-	}
-	cbcEncryptor := cipher.NewCBCEncrypter(block, iv)
-	cbcEncryptor.CryptBlocks(plaintext, plaintext)
-	ciphertext := plaintext
-
-	if len(origPlaintext) > aes.BlockSize {
-		swapFinalTwoAESBlocks(ciphertext)
-
-		if len(origPlaintext)%16 != 0 {
-			// Truncate the ciphertext
-			ciphertext = ciphertext[:len(ciphertext)-aes.BlockSize+(len(origPlaintext)%aes.BlockSize)]
-		}
-	}
-
-	if len(ciphertext) != len(origPlaintext) {
-		panic("internal error")
-	}
-
-	return ciphertext
-}
-
-func doCTSDecrypt(key, origCiphertext, iv []byte) []byte {
-	if len(origCiphertext) < aes.BlockSize {
-		panic("input too small")
-	}
-
-	ciphertext := make([]byte, roundUp(len(origCiphertext), aes.BlockSize))
-	copy(ciphertext, origCiphertext)
-
-	if len(ciphertext) > aes.BlockSize {
-		swapFinalTwoAESBlocks(ciphertext)
-	}
-
-	block, err := aes.NewCipher(key)
-	if err != nil {
-		panic(err)
-	}
-	cbcDecrypter := cipher.NewCBCDecrypter(block, iv)
-
-	var plaintext []byte
-	if overhang := len(origCiphertext) % aes.BlockSize; overhang == 0 {
-		cbcDecrypter.CryptBlocks(ciphertext, ciphertext)
-		plaintext = ciphertext
-	} else {
-		ciphertext, finalBlock := ciphertext[:len(ciphertext)-aes.BlockSize], ciphertext[len(ciphertext)-aes.BlockSize:]
-		var plaintextFinalBlock [aes.BlockSize]byte
-		block.Decrypt(plaintextFinalBlock[:], finalBlock)
-		copy(ciphertext[len(ciphertext)-aes.BlockSize+overhang:], plaintextFinalBlock[overhang:])
-		plaintext = make([]byte, len(origCiphertext))
-		cbcDecrypter.CryptBlocks(plaintext, ciphertext)
-		for i := 0; i < overhang; i++ {
-			plaintextFinalBlock[i] ^= ciphertext[len(ciphertext)-aes.BlockSize+i]
-		}
-		copy(plaintext[len(ciphertext):], plaintextFinalBlock[:overhang])
-	}
-
-	return plaintext
-}
-
-func ctsEncrypt(args [][]byte) error {
-	if len(args) != 4 {
-		return fmt.Errorf("ctsEncrypt received %d args, wanted 4", len(args))
-	}
-
-	key, plaintext, iv, numIterations32 := args[0], args[1], args[2], args[3]
-	if len(numIterations32) != 4 || binary.LittleEndian.Uint32(numIterations32) != 1 {
-		return errors.New("only a single iteration supported for ctsEncrypt")
-	}
-
-	if len(plaintext) < aes.BlockSize {
-		return fmt.Errorf("ctsEncrypt plaintext too short: %d bytes", len(plaintext))
-	}
-
-	return reply(doCTSEncrypt(key, plaintext, iv))
-}
-
-func ctsDecrypt(args [][]byte) error {
-	if len(args) != 4 {
-		return fmt.Errorf("ctsDecrypt received %d args, wanted 4", len(args))
-	}
-
-	key, ciphertext, iv, numIterations32 := args[0], args[1], args[2], args[3]
-	if len(numIterations32) != 4 || binary.LittleEndian.Uint32(numIterations32) != 1 {
-		return errors.New("only a single iteration supported for ctsDecrypt")
-	}
-
-	if len(ciphertext) < aes.BlockSize {
-		return errors.New("ctsDecrypt ciphertext too short")
-	}
-
-	return reply(doCTSDecrypt(key, ciphertext, iv))
-}
-
 const (
-	maxArgs       = 9
+	maxArgs       = 8
 	maxArgLength  = 1 << 20
 	maxNameLength = 30
 )
diff --git a/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc
index 7188029..1974dce 100644
--- a/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc
+++ b/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -54,7 +54,6 @@
 #if defined(OPENSSL_TRUSTY)
 #include <trusty_log.h>
 #define LOG_ERROR(...) TLOGE(__VA_ARGS__)
-#define TLOG_TAG "modulewrapper"
 #else
 #define LOG_ERROR(...) fprintf(stderr, __VA_ARGS__)
 #endif  // OPENSSL_TRUSTY
diff --git a/src/util/generate_build_files.py b/src/util/generate_build_files.py
index 5cc2de4..5ec2d6a 100644
--- a/src/util/generate_build_files.py
+++ b/src/util/generate_build_files.py
@@ -472,7 +472,7 @@
 # builds.
 if(NOT OPENSSL_NO_ASM AND CMAKE_OSX_ARCHITECTURES)
   list(LENGTH CMAKE_OSX_ARCHITECTURES NUM_ARCHES)
-  if(NOT NUM_ARCHES EQUAL 1)
+  if(NOT ${NUM_ARCHES} EQUAL 1)
     message(FATAL_ERROR "Universal binaries not supported.")
   endif()
   list(GET CMAKE_OSX_ARCHITECTURES 0 CMAKE_SYSTEM_PROCESSOR)
@@ -481,36 +481,36 @@
 if(OPENSSL_NO_ASM)
   add_definitions(-DOPENSSL_NO_ASM)
   set(ARCH "generic")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
   set(ARCH "x86_64")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "amd64")
   set(ARCH "x86_64")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64")
   # cmake reports AMD64 on Windows, but we might be building for 32-bit.
   if(CMAKE_SIZEOF_VOID_P EQUAL 8)
     set(ARCH "x86_64")
   else()
     set(ARCH "x86")
   endif()
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86")
   set(ARCH "x86")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386")
   set(ARCH "x86")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i686")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686")
   set(ARCH "x86")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
   set(ARCH "aarch64")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64")
   set(ARCH "aarch64")
 # Apple A12 Bionic chipset which is added in iPhone XS/XS Max/XR uses arm64e architecture.
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64e")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64e")
   set(ARCH "aarch64")
-elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm*")
+elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm*")
   set(ARCH "arm")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "mips")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "mips")
   # Just to avoid the “unknown processor” error.
   set(ARCH "generic")
-elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le")
+elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64le")
   set(ARCH "ppc64le")
 else()
   message(FATAL_ERROR "Unknown processor:" ${CMAKE_SYSTEM_PROCESSOR})
@@ -586,9 +586,9 @@
             asm_files)
 
       cmake.write(
-R'''if(APPLE AND ARCH STREQUAL "aarch64")
+R'''if(APPLE AND ${ARCH} STREQUAL "aarch64")
   set(CRYPTO_ARCH_SOURCES ${CRYPTO_ios_aarch64_SOURCES})
-elseif(APPLE AND ARCH STREQUAL "arm")
+elseif(APPLE AND ${ARCH} STREQUAL "arm")
   set(CRYPTO_ARCH_SOURCES ${CRYPTO_ios_arm_SOURCES})
 elseif(APPLE)
   set(CRYPTO_ARCH_SOURCES ${CRYPTO_mac_${ARCH}_SOURCES})
diff --git a/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index 7cba4da..75f7b64 100644
--- a/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -15,7 +15,6 @@
 #endif
 #include <openssl/arm_arch.h>
 
-#if __ARM_MAX_ARCH__>=7
 .text
 .arch	armv8-a+crypto
 .globl	gcm_init_v8
@@ -68,48 +67,8 @@
 	ext	v17.16b,v22.16b,v22.16b,#8		//Karatsuba pre-processing
 	eor	v17.16b,v17.16b,v22.16b
 	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v21.2d,v22.2d},[x0],#32	//store Htable[1..2]
-	//calculate H^3 and H^4
-	pmull	v0.1q,v20.1d, v22.1d
-	pmull	v5.1q,v22.1d,v22.1d
-	pmull2	v2.1q,v20.2d, v22.2d
-	pmull2	v7.1q,v22.2d,v22.2d
-	pmull	v1.1q,v16.1d,v17.1d
-	pmull	v6.1q,v17.1d,v17.1d
+	st1	{v21.2d,v22.2d},[x0]		//store Htable[1..2]
 
-	ext	v16.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	ext	v17.16b,v5.16b,v7.16b,#8
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v16.16b
-	eor	v4.16b,v5.16b,v7.16b
-	eor	v6.16b,v6.16b,v17.16b
-	eor	v1.16b,v1.16b,v18.16b
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase
-	eor	v6.16b,v6.16b,v4.16b
-	pmull	v4.1q,v5.1d,v19.1d
-
-	ins	v2.d[0],v1.d[1]
-	ins	v7.d[0],v6.d[1]
-	ins	v1.d[1],v0.d[0]
-	ins	v6.d[1],v5.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-	eor	v5.16b,v6.16b,v4.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase
-	ext	v4.16b,v5.16b,v5.16b,#8
-	pmull	v0.1q,v0.1d,v19.1d
-	pmull	v5.1q,v5.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v4.16b,v4.16b,v7.16b
-	eor	v20.16b, v0.16b,v18.16b		//H^3
-	eor	v22.16b,v5.16b,v4.16b		//H^4
-
-	ext	v16.16b,v20.16b, v20.16b,#8		//Karatsuba pre-processing
-	ext	v17.16b,v22.16b,v22.16b,#8
-	eor	v16.16b,v16.16b,v20.16b
-	eor	v17.16b,v17.16b,v22.16b
-	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v20.2d,v21.2d,v22.2d},[x0]		//store Htable[3..5]
 	ret
 
 .globl	gcm_gmult_v8
@@ -165,8 +124,6 @@
 .align	4
 gcm_ghash_v8:
 	AARCH64_VALID_CALL_TARGET
-	cmp	x3,#64
-	b.hs	Lgcm_ghash_v8_4x
 	ld1	{v0.2d},[x0]		//load [rotated] Xi
 						//"[rotated]" means that
 						//loaded value would have
@@ -293,291 +250,8 @@
 
 	ret
 
-.def gcm_ghash_v8_4x
-   .type 32
-.endef
-.align	4
-gcm_ghash_v8_4x:
-Lgcm_ghash_v8_4x:
-	ld1	{v0.2d},[x0]		//load [rotated] Xi
-	ld1	{v20.2d,v21.2d,v22.2d},[x1],#48	//load twisted H, ..., H^2
-	movi	v19.16b,#0xe1
-	ld1	{v26.2d,v27.2d,v28.2d},[x1]	//load twisted H^3, ..., H^4
-	shl	v19.2d,v19.2d,#57		//compose 0xc2.0 constant
-
-	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
-#ifndef __ARMEB__
-	rev64	v0.16b,v0.16b
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v7.16b,v7.16b
-	rev64	v4.16b,v4.16b
-#endif
-	ext	v25.16b,v7.16b,v7.16b,#8
-	ext	v24.16b,v6.16b,v6.16b,#8
-	ext	v23.16b,v5.16b,v5.16b,#8
-
-	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
-	eor	v7.16b,v7.16b,v25.16b
-	pmull2	v31.1q,v20.2d,v25.2d
-	pmull	v30.1q,v21.1d,v7.1d
-
-	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-	pmull2	v24.1q,v22.2d,v24.2d
-	pmull2	v6.1q,v21.2d,v6.2d
-
-	eor	v29.16b,v29.16b,v16.16b
-	eor	v31.16b,v31.16b,v24.16b
-	eor	v30.16b,v30.16b,v6.16b
-
-	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	pmull2	v23.1q,v26.2d,v23.2d
-	pmull	v5.1q,v27.1d,v5.1d
-
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	eor	v30.16b,v30.16b,v5.16b
-
-	subs	x3,x3,#128
-	b.lo	Ltail4x
-
-	b	Loop4x
-
-.align	4
-Loop4x:
-	eor	v16.16b,v4.16b,v0.16b
-	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
-	ext	v3.16b,v16.16b,v16.16b,#8
-#ifndef __ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v7.16b,v7.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v28.2d,v3.2d
-	ext	v25.16b,v7.16b,v7.16b,#8
-	pmull2	v1.1q,v27.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	ext	v24.16b,v6.16b,v6.16b,#8
-	eor	v1.16b,v1.16b,v30.16b
-	ext	v23.16b,v5.16b,v5.16b,#8
-
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
-	eor	v7.16b,v7.16b,v25.16b
-	eor	v1.16b,v1.16b,v17.16b
-	pmull2	v31.1q,v20.2d,v25.2d
-	eor	v1.16b,v1.16b,v18.16b
-	pmull	v30.1q,v21.1d,v7.1d
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-	pmull2	v24.1q,v22.2d,v24.2d
-	eor	v0.16b,v1.16b,v18.16b
-	pmull2	v6.1q,v21.2d,v6.2d
-
-	eor	v29.16b,v29.16b,v16.16b
-	eor	v31.16b,v31.16b,v24.16b
-	eor	v30.16b,v30.16b,v6.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	eor	v18.16b,v18.16b,v2.16b
-	pmull2	v23.1q,v26.2d,v23.2d
-	pmull	v5.1q,v27.1d,v5.1d
-
-	eor	v0.16b,v0.16b,v18.16b
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-	eor	v30.16b,v30.16b,v5.16b
-
-	subs	x3,x3,#64
-	b.hs	Loop4x
-
-Ltail4x:
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v28.2d,v3.2d
-	pmull2	v1.1q,v27.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-
-	adds	x3,x3,#64
-	b.eq	Ldone4x
-
-	cmp	x3,#32
-	b.lo	Lone
-	b.eq	Ltwo
-Lthree:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d,v5.2d,v6.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v6.16b,v6.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	ext	v24.16b,v6.16b,v6.16b,#8
-	ext	v23.16b,v5.16b,v5.16b,#8
-	eor	v0.16b,v1.16b,v18.16b
-
-	pmull	v29.1q,v20.1d,v24.1d		//H·Ii+2
-	eor	v6.16b,v6.16b,v24.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	pmull2	v31.1q,v20.2d,v24.2d
-	pmull	v30.1q,v21.1d,v6.1d
-	eor	v0.16b,v0.16b,v18.16b
-	pmull	v7.1q,v22.1d,v23.1d		//H^2·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	pmull2	v23.1q,v22.2d,v23.2d
-	eor	v16.16b,v4.16b,v0.16b
-	pmull2	v5.1q,v21.2d,v5.2d
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	eor	v29.16b,v29.16b,v7.16b
-	eor	v31.16b,v31.16b,v23.16b
-	eor	v30.16b,v30.16b,v5.16b
-
-	pmull	v0.1q,v26.1d,v3.1d		//H^3·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v26.2d,v3.2d
-	pmull	v1.1q,v27.1d,v16.1d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-	b	Ldone4x
-
-.align	4
-Ltwo:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d,v5.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v5.16b,v5.16b
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	ext	v23.16b,v5.16b,v5.16b,#8
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	pmull	v29.1q,v20.1d,v23.1d		//H·Ii+1
-	eor	v5.16b,v5.16b,v23.16b
-
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull2	v31.1q,v20.2d,v23.2d
-	pmull	v30.1q,v21.1d,v5.1d
-
-	pmull	v0.1q,v22.1d,v3.1d		//H^2·(Xi+Ii)
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v22.2d,v3.2d
-	pmull2	v1.1q,v21.2d,v16.2d
-
-	eor	v0.16b,v0.16b,v29.16b
-	eor	v2.16b,v2.16b,v31.16b
-	eor	v1.16b,v1.16b,v30.16b
-	b	Ldone4x
-
-.align	4
-Lone:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	ld1	{v4.2d},[x2]
-	eor	v1.16b,v1.16b,v18.16b
-#ifndef	__ARMEB__
-	rev64	v4.16b,v4.16b
-#endif
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-	eor	v16.16b,v4.16b,v0.16b
-	ext	v3.16b,v16.16b,v16.16b,#8
-
-	pmull	v0.1q,v20.1d,v3.1d
-	eor	v16.16b,v16.16b,v3.16b
-	pmull2	v2.1q,v20.2d,v3.2d
-	pmull	v1.1q,v21.1d,v16.1d
-
-Ldone4x:
-	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
-	eor	v18.16b,v0.16b,v2.16b
-	eor	v1.16b,v1.16b,v17.16b
-	eor	v1.16b,v1.16b,v18.16b
-
-	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
-	ins	v2.d[0],v1.d[1]
-	ins	v1.d[1],v0.d[0]
-	eor	v0.16b,v1.16b,v18.16b
-
-	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
-	pmull	v0.1q,v0.1d,v19.1d
-	eor	v18.16b,v18.16b,v2.16b
-	eor	v0.16b,v0.16b,v18.16b
-	ext	v0.16b,v0.16b,v0.16b,#8
-
-#ifndef __ARMEB__
-	rev64	v0.16b,v0.16b
-#endif
-	st1	{v0.2d},[x0]		//write out Xi
-
-	ret
-
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
 #endif
-#endif
 #endif  // !OPENSSL_NO_ASM