update chromium-stable-with-bazel from chromium-stable branch
diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl
index 369ea07..5e43449 100644
--- a/BUILD.generated.bzl
+++ b/BUILD.generated.bzl
@@ -203,6 +203,7 @@
     "src/crypto/cipher_extra/internal.h",
     "src/crypto/conf/conf_def.h",
     "src/crypto/conf/internal.h",
+    "src/crypto/cpu-arm-linux.h",
     "src/crypto/err/internal.h",
     "src/crypto/evp/internal.h",
     "src/crypto/fipsmodule/aes/internal.h",
diff --git a/BUILD.generated_tests.bzl b/BUILD.generated_tests.bzl
index 6239cbf..8b9fd84 100644
--- a/BUILD.generated_tests.bzl
+++ b/BUILD.generated_tests.bzl
@@ -8,6 +8,7 @@
     "src/crypto/cipher_extra/internal.h",
     "src/crypto/conf/conf_def.h",
     "src/crypto/conf/internal.h",
+    "src/crypto/cpu-arm-linux.h",
     "src/crypto/err/internal.h",
     "src/crypto/evp/internal.h",
     "src/crypto/fipsmodule/aes/internal.h",
@@ -69,6 +70,7 @@
     "src/crypto/cmac/cmac_test.cc",
     "src/crypto/compiler_test.cc",
     "src/crypto/constant_time_test.cc",
+    "src/crypto/cpu-arm-linux_test.cc",
     "src/crypto/curve25519/ed25519_test.cc",
     "src/crypto/curve25519/spake25519_test.cc",
     "src/crypto/curve25519/x25519_test.cc",
@@ -98,9 +100,11 @@
     "src/crypto/pkcs8/pkcs8_test.cc",
     "src/crypto/poly1305/poly1305_test.cc",
     "src/crypto/pool/pool_test.cc",
+    "src/crypto/rand_extra/rand_test.cc",
     "src/crypto/refcount_test.cc",
     "src/crypto/rsa_extra/rsa_test.cc",
     "src/crypto/self_test.cc",
+    "src/crypto/stack/stack_test.cc",
     "src/crypto/test/file_test_gtest.cc",
     "src/crypto/test/gtest_main.cc",
     "src/crypto/thread_test.cc",
diff --git a/err_data.c b/err_data.c
index 09c1041..8c0f0ca 100644
--- a/err_data.c
+++ b/err_data.c
@@ -215,15 +215,16 @@
     0x2c3eaff2,
     0x2c3f300a,
     0x2c3fb022,
-    0x2c40302f,
+    0x2c40304c,
     0x2c4091d0,
-    0x2c413040,
-    0x2c41b053,
+    0x2c41305d,
+    0x2c41b070,
     0x2c421196,
-    0x2c42b064,
+    0x2c42b081,
     0x2c430722,
     0x2c43afa5,
     0x2c442f16,
+    0x2c44b02f,
     0x30320000,
     0x30328015,
     0x3033001f,
@@ -642,69 +643,69 @@
     0x4c41152c,
     0x4c4193af,
     0x4c421518,
-    0x50323076,
-    0x5032b085,
-    0x50333090,
-    0x5033b0a0,
-    0x503430b9,
-    0x5034b0d3,
-    0x503530e1,
-    0x5035b0f7,
-    0x50363109,
-    0x5036b11f,
-    0x50373138,
-    0x5037b14b,
-    0x50383163,
-    0x5038b174,
-    0x50393189,
-    0x5039b19d,
-    0x503a31bd,
-    0x503ab1d3,
-    0x503b31eb,
-    0x503bb1fd,
-    0x503c3219,
-    0x503cb230,
-    0x503d3249,
-    0x503db25f,
-    0x503e326c,
-    0x503eb282,
-    0x503f3294,
+    0x50323093,
+    0x5032b0a2,
+    0x503330ad,
+    0x5033b0bd,
+    0x503430d6,
+    0x5034b0f0,
+    0x503530fe,
+    0x5035b114,
+    0x50363126,
+    0x5036b13c,
+    0x50373155,
+    0x5037b168,
+    0x50383180,
+    0x5038b191,
+    0x503931a6,
+    0x5039b1ba,
+    0x503a31da,
+    0x503ab1f0,
+    0x503b3208,
+    0x503bb21a,
+    0x503c3236,
+    0x503cb24d,
+    0x503d3266,
+    0x503db27c,
+    0x503e3289,
+    0x503eb29f,
+    0x503f32b1,
     0x503f837b,
-    0x504032a7,
-    0x5040b2b7,
-    0x504132d1,
-    0x5041b2e0,
-    0x504232fa,
-    0x5042b317,
-    0x50433327,
-    0x5043b337,
-    0x50443346,
+    0x504032c4,
+    0x5040b2d4,
+    0x504132ee,
+    0x5041b2fd,
+    0x50423317,
+    0x5042b334,
+    0x50433344,
+    0x5043b354,
+    0x50443363,
     0x50448431,
-    0x5045335a,
-    0x5045b378,
-    0x5046338b,
-    0x5046b3a1,
-    0x504733b3,
-    0x5047b3c8,
-    0x504833ee,
-    0x5048b3fc,
-    0x5049340f,
-    0x5049b424,
-    0x504a343a,
-    0x504ab44a,
-    0x504b346a,
-    0x504bb47d,
-    0x504c34a0,
-    0x504cb4ce,
-    0x504d34e0,
-    0x504db4fd,
-    0x504e3518,
-    0x504eb534,
-    0x504f3546,
-    0x504fb55d,
-    0x5050356c,
+    0x50453377,
+    0x5045b395,
+    0x504633a8,
+    0x5046b3be,
+    0x504733d0,
+    0x5047b3e5,
+    0x5048340b,
+    0x5048b419,
+    0x5049342c,
+    0x5049b441,
+    0x504a3457,
+    0x504ab467,
+    0x504b3487,
+    0x504bb49a,
+    0x504c34bd,
+    0x504cb4eb,
+    0x504d34fd,
+    0x504db51a,
+    0x504e3535,
+    0x504eb551,
+    0x504f3563,
+    0x504fb57a,
+    0x50503589,
     0x505086f1,
-    0x5051357f,
+    0x5051359c,
     0x58320f52,
     0x68320f14,
     0x68328c6c,
@@ -1322,6 +1323,7 @@
     "PUBLIC_KEY_DECODE_ERROR\0"
     "PUBLIC_KEY_ENCODE_ERROR\0"
     "SHOULD_RETRY\0"
+    "SIGNATURE_ALGORITHM_MISMATCH\0"
     "UNKNOWN_KEY_TYPE\0"
     "UNKNOWN_PURPOSE_ID\0"
     "UNKNOWN_TRUST_ID\0"
diff --git a/ios-aarch64/crypto/chacha/chacha-armv8.S b/ios-aarch64/crypto/chacha/chacha-armv8.S
index 2647b38..1cb00b7 100644
--- a/ios-aarch64/crypto/chacha/chacha-armv8.S
+++ b/ios-aarch64/crypto/chacha/chacha-armv8.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -1974,4 +1977,4 @@
 	ldp	x29,x30,[sp],#96
 	ret
 
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-aarch64/crypto/fipsmodule/aesv8-armx64.S b/ios-aarch64/crypto/fipsmodule/aesv8-armx64.S
index 734bf41..fe4742f 100644
--- a/ios-aarch64/crypto/fipsmodule/aesv8-armx64.S
+++ b/ios-aarch64/crypto/fipsmodule/aesv8-armx64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 #if __ARM_MAX_ARCH__>=7
@@ -758,4 +761,4 @@
 	ret
 
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-aarch64/crypto/fipsmodule/armv8-mont.S b/ios-aarch64/crypto/fipsmodule/armv8-mont.S
index 60bda42..d228592 100644
--- a/ios-aarch64/crypto/fipsmodule/armv8-mont.S
+++ b/ios-aarch64/crypto/fipsmodule/armv8-mont.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text
 
 .globl	_bn_mul_mont
@@ -1410,4 +1413,4 @@
 .byte	77,111,110,116,103,111,109,101,114,121,32,77,117,108,116,105,112,108,105,99,97,116,105,111,110,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	4
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index e8a897f..79a1df5 100644
--- a/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -236,4 +239,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  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-aarch64/crypto/fipsmodule/sha1-armv8.S b/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
index d3b4c61..17e1a56 100644
--- a/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/ios-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -1227,4 +1230,5 @@
 .align	2
 .align	2
 .comm	_OPENSSL_armcap_P,4,4
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+.private_extern	_OPENSSL_armcap_P
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-aarch64/crypto/fipsmodule/sha256-armv8.S b/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
index 4be4f86..9076eeb 100644
--- a/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/ios-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 // Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
 //
 // Licensed under the OpenSSL license (the "License").  You may not use
@@ -1205,5 +1208,6 @@
 #endif
 #ifndef	__KERNEL__
 .comm	_OPENSSL_armcap_P,4,4
+.private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-aarch64/crypto/fipsmodule/sha512-armv8.S b/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
index da80442..d4fd317 100644
--- a/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/ios-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 // Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
 //
 // Licensed under the OpenSSL license (the "License").  You may not use
@@ -1077,5 +1080,6 @@
 .align	2
 #ifndef	__KERNEL__
 .comm	_OPENSSL_armcap_P,4,4
+.private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/chacha/chacha-armv4.S b/ios-arm/crypto/chacha/chacha-armv4.S
index 0629e94..9f86ad5 100644
--- a/ios-arm/crypto/chacha/chacha-armv4.S
+++ b/ios-arm/crypto/chacha/chacha-armv4.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 @ Silence ARMv8 deprecated IT instruction warnings. This file is used by both
@@ -1488,4 +1491,4 @@
 .indirect_symbol	_OPENSSL_armcap_P
 .long	0
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/aes-armv4.S b/ios-arm/crypto/fipsmodule/aes-armv4.S
index 97c4829..6543d8c 100644
--- a/ios-arm/crypto/fipsmodule/aes-armv4.S
+++ b/ios-arm/crypto/fipsmodule/aes-armv4.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 @ Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
 @
 @ Licensed under the OpenSSL license (the "License").  You may not use
@@ -1223,4 +1226,4 @@
 .byte	65,69,83,32,102,111,114,32,65,82,77,118,52,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  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/aesv8-armx32.S b/ios-arm/crypto/fipsmodule/aesv8-armx32.S
index 64724c4..aafcea1 100644
--- a/ios-arm/crypto/fipsmodule/aesv8-armx32.S
+++ b/ios-arm/crypto/fipsmodule/aesv8-armx32.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 #if __ARM_MAX_ARCH__>=7
@@ -778,4 +781,4 @@
 	ldmia	sp!,{r4,r5,r6,r7,r8,r9,r10,pc}
 
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/armv4-mont.S b/ios-arm/crypto/fipsmodule/armv4-mont.S
index 0ade602..7a502f0 100644
--- a/ios-arm/crypto/fipsmodule/armv4-mont.S
+++ b/ios-arm/crypto/fipsmodule/armv4-mont.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 @ Silence ARMv8 deprecated IT instruction warnings. This file is used by both
@@ -972,4 +975,4 @@
 .long	0
 .private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/bsaes-armv7.S b/ios-arm/crypto/fipsmodule/bsaes-armv7.S
index 8338cf4..beb294c 100644
--- a/ios-arm/crypto/fipsmodule/bsaes-armv7.S
+++ b/ios-arm/crypto/fipsmodule/bsaes-armv7.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 @ Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
 @
 @ Licensed under the OpenSSL license (the "License").  You may not use
@@ -2580,4 +2583,4 @@
 
 
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/ghash-armv4.S b/ios-arm/crypto/fipsmodule/ghash-armv4.S
index 8c5c6cb..efb018c 100644
--- a/ios-arm/crypto/fipsmodule/ghash-armv4.S
+++ b/ios-arm/crypto/fipsmodule/ghash-armv4.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 @ Silence ARMv8 deprecated IT instruction warnings. This file is used by both
@@ -593,4 +596,4 @@
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,52,47,78,69,79,78,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  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/ghashv8-armx32.S b/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
index 10fd661..946293a 100644
--- a/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
+++ b/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -246,4 +249,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  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/sha1-armv4-large.S b/ios-arm/crypto/fipsmodule/sha1-armv4-large.S
index 3f5c54b..8b1790a 100644
--- a/ios-arm/crypto/fipsmodule/sha1-armv4-large.S
+++ b/ios-arm/crypto/fipsmodule/sha1-armv4-large.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -1508,4 +1511,4 @@
 .long	0
 .private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/sha256-armv4.S b/ios-arm/crypto/fipsmodule/sha256-armv4.S
index 562edac..a101952 100644
--- a/ios-arm/crypto/fipsmodule/sha256-armv4.S
+++ b/ios-arm/crypto/fipsmodule/sha256-armv4.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 @ Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
 @
 @ Licensed under the OpenSSL license (the "License").  You may not use
@@ -2836,4 +2839,4 @@
 .long	0
 .private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/ios-arm/crypto/fipsmodule/sha512-armv4.S b/ios-arm/crypto/fipsmodule/sha512-armv4.S
index dedbce6..ddaa1a0 100644
--- a/ios-arm/crypto/fipsmodule/sha512-armv4.S
+++ b/ios-arm/crypto/fipsmodule/sha512-armv4.S
@@ -5,6 +5,9 @@
 #endif
 
 #if !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 @ Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
 @
 @ Licensed under the OpenSSL license (the "License").  You may not use
@@ -1889,4 +1892,4 @@
 .long	0
 .private_extern	_OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/chacha/chacha-armv8.S b/linux-aarch64/crypto/chacha/chacha-armv8.S
index cf7a162..1832a30 100644
--- a/linux-aarch64/crypto/chacha/chacha-armv8.S
+++ b/linux-aarch64/crypto/chacha/chacha-armv8.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__aarch64__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -1976,4 +1979,4 @@
 	ret
 .size	ChaCha20_512_neon,.-ChaCha20_512_neon
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S b/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S
index 48b7170..0e5526f 100644
--- a/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S
+++ b/linux-aarch64/crypto/fipsmodule/aesv8-armx64.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__aarch64__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 #if __ARM_MAX_ARCH__>=7
@@ -760,4 +763,4 @@
 .size	aes_hw_ctr32_encrypt_blocks,.-aes_hw_ctr32_encrypt_blocks
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/armv8-mont.S b/linux-aarch64/crypto/fipsmodule/armv8-mont.S
index ec3ad6e..c65dd18 100644
--- a/linux-aarch64/crypto/fipsmodule/armv8-mont.S
+++ b/linux-aarch64/crypto/fipsmodule/armv8-mont.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__aarch64__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text
 
 .globl	bn_mul_mont
@@ -1412,4 +1415,4 @@
 .align	2
 .align	4
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index 8e5374f..f3ae6c7 100644
--- a/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__aarch64__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -238,4 +241,4 @@
 .align	2
 .align	2
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/sha1-armv8.S b/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
index bb64930..3b6cf6a 100644
--- a/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
+++ b/linux-aarch64/crypto/fipsmodule/sha1-armv8.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__aarch64__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -1228,5 +1231,6 @@
 .align	2
 .align	2
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/sha256-armv8.S b/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
index 3a06594..8bb535c 100644
--- a/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
+++ b/linux-aarch64/crypto/fipsmodule/sha256-armv8.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__aarch64__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 // Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
 //
 // Licensed under the OpenSSL license (the "License").  You may not use
@@ -1206,6 +1209,7 @@
 #endif
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-aarch64/crypto/fipsmodule/sha512-armv8.S b/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
index c04e0e8..ac9d5f0 100644
--- a/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
+++ b/linux-aarch64/crypto/fipsmodule/sha512-armv8.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__aarch64__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 // Copyright 2014-2016 The OpenSSL Project Authors. All Rights Reserved.
 //
 // Licensed under the OpenSSL license (the "License").  You may not use
@@ -1078,6 +1081,7 @@
 .align	2
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/chacha/chacha-armv4.S b/linux-arm/crypto/chacha/chacha-armv4.S
index 309a5b1..489021c 100644
--- a/linux-arm/crypto/chacha/chacha-armv4.S
+++ b/linux-arm/crypto/chacha/chacha-armv4.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 @ Silence ARMv8 deprecated IT instruction warnings. This file is used by both
@@ -1482,4 +1485,4 @@
 .comm	OPENSSL_armcap_P,4,4
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/aes-armv4.S b/linux-arm/crypto/fipsmodule/aes-armv4.S
index bfcc5d8..cdd91ae 100644
--- a/linux-arm/crypto/fipsmodule/aes-armv4.S
+++ b/linux-arm/crypto/fipsmodule/aes-armv4.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 @ Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
 @
 @ Licensed under the OpenSSL license (the "License").  You may not use
@@ -1211,4 +1214,4 @@
 .align	2
 .align	2
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/aesv8-armx32.S b/linux-arm/crypto/fipsmodule/aesv8-armx32.S
index bd90ab5..bdda4c3 100644
--- a/linux-arm/crypto/fipsmodule/aesv8-armx32.S
+++ b/linux-arm/crypto/fipsmodule/aesv8-armx32.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 #if __ARM_MAX_ARCH__>=7
@@ -768,4 +771,4 @@
 .size	aes_hw_ctr32_encrypt_blocks,.-aes_hw_ctr32_encrypt_blocks
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/armv4-mont.S b/linux-arm/crypto/fipsmodule/armv4-mont.S
index 95be293..6a01ae0 100644
--- a/linux-arm/crypto/fipsmodule/armv4-mont.S
+++ b/linux-arm/crypto/fipsmodule/armv4-mont.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 @ Silence ARMv8 deprecated IT instruction warnings. This file is used by both
@@ -966,4 +969,4 @@
 .hidden	OPENSSL_armcap_P
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/bsaes-armv7.S b/linux-arm/crypto/fipsmodule/bsaes-armv7.S
index 7b26afb..4b53131 100644
--- a/linux-arm/crypto/fipsmodule/bsaes-armv7.S
+++ b/linux-arm/crypto/fipsmodule/bsaes-armv7.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 @ Copyright 2012-2016 The OpenSSL Project Authors. All Rights Reserved.
 @
 @ Licensed under the OpenSSL license (the "License").  You may not use
@@ -2568,4 +2571,4 @@
 .size	bsaes_xts_decrypt,.-bsaes_xts_decrypt
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/ghash-armv4.S b/linux-arm/crypto/fipsmodule/ghash-armv4.S
index 76155f0..99b2bc3 100644
--- a/linux-arm/crypto/fipsmodule/ghash-armv4.S
+++ b/linux-arm/crypto/fipsmodule/ghash-armv4.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 @ Silence ARMv8 deprecated IT instruction warnings. This file is used by both
@@ -583,4 +586,4 @@
 .align	2
 .align	2
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/ghashv8-armx32.S b/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
index 4e54b1d..10a9b4d 100644
--- a/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
+++ b/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -242,4 +245,4 @@
 .align	2
 .align	2
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/sha1-armv4-large.S b/linux-arm/crypto/fipsmodule/sha1-armv4-large.S
index 7cfbf6e..e168d68 100644
--- a/linux-arm/crypto/fipsmodule/sha1-armv4-large.S
+++ b/linux-arm/crypto/fipsmodule/sha1-armv4-large.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 #include <openssl/arm_arch.h>
 
 .text
@@ -1500,4 +1503,4 @@
 .hidden	OPENSSL_armcap_P
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/sha256-armv4.S b/linux-arm/crypto/fipsmodule/sha256-armv4.S
index 52be2d2..e5092df 100644
--- a/linux-arm/crypto/fipsmodule/sha256-armv4.S
+++ b/linux-arm/crypto/fipsmodule/sha256-armv4.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 @ Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
 @
 @ Licensed under the OpenSSL license (the "License").  You may not use
@@ -2828,4 +2831,4 @@
 .hidden	OPENSSL_armcap_P
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-arm/crypto/fipsmodule/sha512-armv4.S b/linux-arm/crypto/fipsmodule/sha512-armv4.S
index 00b1c34..5f1b67e 100644
--- a/linux-arm/crypto/fipsmodule/sha512-armv4.S
+++ b/linux-arm/crypto/fipsmodule/sha512-armv4.S
@@ -6,6 +6,9 @@
 
 #if !defined(OPENSSL_NO_ASM)
 #if defined(__arm__)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 @ Copyright 2007-2016 The OpenSSL Project Authors. All Rights Reserved.
 @
 @ Licensed under the OpenSSL license (the "License").  You may not use
@@ -1883,4 +1886,4 @@
 .hidden	OPENSSL_armcap_P
 #endif
 #endif
-#endif  // !OPENSSL_NO_ASM
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM
diff --git a/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S b/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S
index 2403685..618bd1c 100644
--- a/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S
+++ b/linux-ppc64le/crypto/fipsmodule/aesp8-ppc.S
@@ -1,6 +1,6 @@
 #if defined(__has_feature)
-#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)"
-#define OPENSSL_NO_ASM"
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
 #endif
 #endif
 
@@ -3663,4 +3663,4 @@
 	blr	
 .long	0
 .byte	0,12,0x14,0,0,0,0,0
-#endif  // !OPENSSL_NO_ASM && __powerpc64__
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM && __powerpc64__
diff --git a/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S b/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S
index d424f5c..f4f9d25 100644
--- a/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S
+++ b/linux-ppc64le/crypto/fipsmodule/ghashp8-ppc.S
@@ -1,6 +1,6 @@
 #if defined(__has_feature)
-#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)"
-#define OPENSSL_NO_ASM"
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
 #endif
 #endif
 
@@ -580,4 +580,4 @@
 .byte	71,72,65,83,72,32,102,111,114,32,80,111,119,101,114,73,83,65,32,50,46,48,55,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  // !OPENSSL_NO_ASM && __powerpc64__
\ No newline at end of file
+#endif  // !OPENSSL_NO_ASM && __powerpc64__
diff --git a/linux-x86_64/crypto/chacha/chacha-x86_64.S b/linux-x86_64/crypto/chacha/chacha-x86_64.S
index 378e45e..c202957 100644
--- a/linux-x86_64/crypto/chacha/chacha-x86_64.S
+++ b/linux-x86_64/crypto/chacha/chacha-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .extern	OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S b/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
index a925783..0d5b7ec 100644
--- a/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
+++ b/linux-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .data	
 
 .align	16
diff --git a/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S b/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
index 001d396..a466f13 100644
--- a/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
+++ b/linux-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 .extern	OPENSSL_ia32cap_P
 .hidden OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/fipsmodule/aes-x86_64.S b/linux-x86_64/crypto/fipsmodule/aes-x86_64.S
index 517d6b9..43b4e7d 100644
--- a/linux-x86_64/crypto/fipsmodule/aes-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/aes-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 .type	_x86_64_AES_encrypt,@function
 .align	16
diff --git a/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S b/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
index 69a8fc2..1b7ef99 100644
--- a/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .type	_aesni_ctr32_ghash_6x,@function
diff --git a/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S b/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S
index c3f00d8..44d5156 100644
--- a/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/aesni-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 .extern	OPENSSL_ia32cap_P
 .hidden OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/fipsmodule/bsaes-x86_64.S b/linux-x86_64/crypto/fipsmodule/bsaes-x86_64.S
index 68de37a..bc69c5d 100644
--- a/linux-x86_64/crypto/fipsmodule/bsaes-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/bsaes-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .extern	aes_nohw_encrypt
diff --git a/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S b/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S
index 14c6672..00804c2 100644
--- a/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/ghash-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 .extern	OPENSSL_ia32cap_P
 .hidden OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/fipsmodule/md5-x86_64.S b/linux-x86_64/crypto/fipsmodule/md5-x86_64.S
index e1d64d4..0713f96 100644
--- a/linux-x86_64/crypto/fipsmodule/md5-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/md5-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 .align	16
 
diff --git a/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S b/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
index a2dcc21..267e436 100644
--- a/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
+++ b/linux-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 .extern	OPENSSL_ia32cap_P
 .hidden OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S b/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S
index 216ed27..dc0d43c 100644
--- a/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/rdrand-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S b/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S
index a13f531..8dd5137 100644
--- a/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S
+++ b/linux-x86_64/crypto/fipsmodule/rsaz-avx2.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .globl	rsaz_1024_sqr_avx2
diff --git a/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S b/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S
index 4e9fbf5..d9629ea 100644
--- a/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/sha1-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 .extern	OPENSSL_ia32cap_P
 .hidden OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S b/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S
index 961d349..c08afe8 100644
--- a/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/sha256-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .extern	OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S b/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S
index b130808..e2551fc 100644
--- a/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/sha512-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .extern	OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S b/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S
index e66e99d..91b7aae 100644
--- a/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S
+++ b/linux-x86_64/crypto/fipsmodule/vpaes-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/linux-x86_64/crypto/fipsmodule/x86_64-mont.S b/linux-x86_64/crypto/fipsmodule/x86_64-mont.S
index d077afc..7bcfbe9 100644
--- a/linux-x86_64/crypto/fipsmodule/x86_64-mont.S
+++ b/linux-x86_64/crypto/fipsmodule/x86_64-mont.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .extern	OPENSSL_ia32cap_P
diff --git a/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S b/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S
index 3c6cdc5..d98a045 100644
--- a/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S
+++ b/linux-x86_64/crypto/fipsmodule/x86_64-mont5.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .extern	OPENSSL_ia32cap_P
diff --git a/mac-x86_64/crypto/chacha/chacha-x86_64.S b/mac-x86_64/crypto/chacha/chacha-x86_64.S
index a5fe611..7b428ec 100644
--- a/mac-x86_64/crypto/chacha/chacha-x86_64.S
+++ b/mac-x86_64/crypto/chacha/chacha-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S b/mac-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
index 29212e9..0b51d06 100644
--- a/mac-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
+++ b/mac-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .data	
 
 .p2align	4
diff --git a/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S b/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
index 611922f..d2a1ed2 100644
--- a/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
+++ b/mac-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/aes-x86_64.S b/mac-x86_64/crypto/fipsmodule/aes-x86_64.S
index da0b70c..6e3b750 100644
--- a/mac-x86_64/crypto/fipsmodule/aes-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/aes-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .p2align	4
diff --git a/mac-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S b/mac-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
index b491eb4..816ea86 100644
--- a/mac-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/aesni-x86_64.S b/mac-x86_64/crypto/fipsmodule/aesni-x86_64.S
index d0fa581..b9788be 100644
--- a/mac-x86_64/crypto/fipsmodule/aesni-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/aesni-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .globl	_aes_hw_encrypt
diff --git a/mac-x86_64/crypto/fipsmodule/bsaes-x86_64.S b/mac-x86_64/crypto/fipsmodule/bsaes-x86_64.S
index 3995e3d..f144752 100644
--- a/mac-x86_64/crypto/fipsmodule/bsaes-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/bsaes-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/ghash-x86_64.S b/mac-x86_64/crypto/fipsmodule/ghash-x86_64.S
index a32c336..835abc4 100644
--- a/mac-x86_64/crypto/fipsmodule/ghash-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/ghash-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/md5-x86_64.S b/mac-x86_64/crypto/fipsmodule/md5-x86_64.S
index d4d6b78..829cc00 100644
--- a/mac-x86_64/crypto/fipsmodule/md5-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/md5-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 .p2align	4
 
diff --git a/mac-x86_64/crypto/fipsmodule/p256-x86_64-asm.S b/mac-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
index 34d0aa6..03890a9 100644
--- a/mac-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
+++ b/mac-x86_64/crypto/fipsmodule/p256-x86_64-asm.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/rdrand-x86_64.S b/mac-x86_64/crypto/fipsmodule/rdrand-x86_64.S
index 9622854..f9dfcbf 100644
--- a/mac-x86_64/crypto/fipsmodule/rdrand-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/rdrand-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/rsaz-avx2.S b/mac-x86_64/crypto/fipsmodule/rsaz-avx2.S
index a8e7e1f..d31293f 100644
--- a/mac-x86_64/crypto/fipsmodule/rsaz-avx2.S
+++ b/mac-x86_64/crypto/fipsmodule/rsaz-avx2.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 .globl	_rsaz_1024_sqr_avx2
diff --git a/mac-x86_64/crypto/fipsmodule/sha1-x86_64.S b/mac-x86_64/crypto/fipsmodule/sha1-x86_64.S
index a0faa47..59423b2 100644
--- a/mac-x86_64/crypto/fipsmodule/sha1-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/sha1-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/sha256-x86_64.S b/mac-x86_64/crypto/fipsmodule/sha256-x86_64.S
index 5508138..ea50ea8 100644
--- a/mac-x86_64/crypto/fipsmodule/sha256-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/sha256-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/sha512-x86_64.S b/mac-x86_64/crypto/fipsmodule/sha512-x86_64.S
index 2e2b8de..68c30f3 100644
--- a/mac-x86_64/crypto/fipsmodule/sha512-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/sha512-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/vpaes-x86_64.S b/mac-x86_64/crypto/fipsmodule/vpaes-x86_64.S
index ad1a5e8..e582f15 100644
--- a/mac-x86_64/crypto/fipsmodule/vpaes-x86_64.S
+++ b/mac-x86_64/crypto/fipsmodule/vpaes-x86_64.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/x86_64-mont.S b/mac-x86_64/crypto/fipsmodule/x86_64-mont.S
index 716b1a8..6d0ea6d 100644
--- a/mac-x86_64/crypto/fipsmodule/x86_64-mont.S
+++ b/mac-x86_64/crypto/fipsmodule/x86_64-mont.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/mac-x86_64/crypto/fipsmodule/x86_64-mont5.S b/mac-x86_64/crypto/fipsmodule/x86_64-mont5.S
index 2755492..0db3c3c 100644
--- a/mac-x86_64/crypto/fipsmodule/x86_64-mont5.S
+++ b/mac-x86_64/crypto/fipsmodule/x86_64-mont5.S
@@ -5,6 +5,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 .text	
 
 
diff --git a/src/.gitignore b/src/.gitignore
index db50b0b..9cb7251 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -17,7 +17,6 @@
 util/bot/gyp
 util/bot/libcxx
 util/bot/libcxxabi
-util/bot/libFuzzer
 util/bot/llvm-build
 util/bot/nasm-win32.exe
 util/bot/perl-win32
diff --git a/src/API-CONVENTIONS.md b/src/API-CONVENTIONS.md
index 7b33797..e322249 100644
--- a/src/API-CONVENTIONS.md
+++ b/src/API-CONVENTIONS.md
@@ -98,7 +98,10 @@
 `openssl/base.h`. Code that needs access to the free functions, such as code
 which destroys a `bssl::UniquePtr`, must include the corresponding module's
 header. (This matches `std::unique_ptr`'s relationship with forward
-declarations.)
+declarations.) Note, despite the name, `bssl::UniquePtr` is also used with
+reference-counted types. It owns a single reference to the object. To take an
+additional reference, use the `bssl::UpRef` function, which will return a
+separate `bssl::UniquePtr`.
 
 
 ### Stack-allocated types
@@ -175,6 +178,67 @@
 used freely without special cleanup conventions.
 
 
+### Ownership and lifetime
+
+When working with allocated objects, it is important to think about *ownership*
+of each object, or what code is responsible for releasing it. This matches the
+corresponding notion in higher-level languages like C++ and Rust.
+
+Ownership applies to both uniquely-owned types and reference-counted types. For
+the latter, ownership means the code is responsible for releasing one
+reference. Note a *reference* in BoringSSL refers to an increment (and eventual
+decrement) of an object's reference count, not `T&` in C++. Thus, to "take a
+reference" means to increment the reference count and take ownership of
+decrementing it.
+
+As BoringSSL's APIs are primarily in C, ownership and lifetime obligations are
+not rigorously annotated in the type signatures or checked at compile-time.
+Instead, they are described in
+[API documentation](https://commondatastorage.googleapis.com/chromium-boringssl-docs/headers.html).
+This section describes some conventions.
+
+Unless otherwise documented, functions do not take ownership of pointer
+arguments. The pointer typically must remain valid for the duration of the
+function call. The function may internally copy information from the argument or
+take a reference, but the caller is free to release its copy or reference at any
+point after the call completes.
+
+A function may instead be documented to *take* or *transfer* ownership of a
+pointer. The caller must own the object before the function call and, after
+transfer, no longer owns it. As a corollary, the caller may no longer reference
+the object without a separate guarantee on the lifetime. The function may even
+release the object before returning. Callers that wish to independently retain a
+transfered object must therefore take a reference or make a copy before
+transferring. Callers should also take note of whether the function is
+documented to transfer pointers unconditionally or only on success. Unlike C++
+and Rust, functions in BoringSSL typically only transfer on success.
+
+Likewise, output pointers may be owning or non-owning. Unless otherwise
+documented, functions output non-owning pointers. The caller is not responsible
+for releasing the output pointer, but it must not use the pointer beyond its
+lifetime. The pointer may be released when the parent object is released or even
+sooner on state change in the parent object.
+
+If documented to output a *newly-allocated* object or a *reference* or *copy* of
+one, the caller is responsible for releasing the object when it is done.
+
+By convention, functions named `get0` return non-owning pointers. Functions
+named `new` or `get1` return owning pointers. Functions named `set0` take
+ownership of arguments. Functions named `set1` do not. They typically take a
+reference or make a copy internally. These names originally referred to the
+effect on a reference count, but the convention applies equally to
+non-reference-counted types.
+
+API documentation may also describe more complex obligations. For instance, an
+object may borrow a pointer for longer than the duration of a single function
+call, in which case the caller must ensure the lifetime extends accordingly.
+
+Memory errors are one of the most common and dangerous bugs in C and C++, so
+callers are encouraged to make use of tools such as
+[AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) and
+higher-level languages.
+
+
 ## Thread safety
 
 BoringSSL is internally aware of the platform threading library and calls into
diff --git a/src/BUILDING.md b/src/BUILDING.md
index 19dbe01..01f5480 100644
--- a/src/BUILDING.md
+++ b/src/BUILDING.md
@@ -29,8 +29,9 @@
     and Clang should work on non-Windows platforms, and maybe on Windows too.
     To build the tests, you also need a C++ compiler with C++11 support.
 
-  * [Go](https://golang.org/dl/) is required. If not found by CMake, the go
-    executable may be configured explicitly by setting `GO_EXECUTABLE`.
+  * The most recent stable version of [Go](https://golang.org/dl/) is required.
+    If not found by CMake, the go executable may be configured explicitly by
+    setting `GO_EXECUTABLE`.
 
   * To build the x86 and x86\_64 assembly, your assembler must support AVX2
     instructions and MOVBE. If using GNU binutils, you must have 2.22 or later
@@ -110,6 +111,32 @@
 Passing multiple architectures for a multiple-architecture build is not
 supported.
 
+### Building with Prefixed Symbols
+
+BoringSSL's build system has experimental support for adding a custom prefix to
+all symbols. This can be useful when linking multiple versions of BoringSSL in
+the same project to avoid symbol conflicts.
+
+In order to build with prefixed symbols, the `BORINGSSL_PREFIX` CMake variable
+should specify the prefix to add to all symbols, and the
+`BORINGSSL_PREFIX_SYMBOLS` CMake variable should specify the path to a file
+which contains a list of symbols which should be prefixed (one per line;
+comments are supported with `#`). In other words, `cmake ..
+-DBORINGSSL_PREFIX=MY_CUSTOM_PREFIX
+-DBORINGSSL_PREFIX_SYMBOLS=/path/to/symbols.txt` will configure the build to add
+the prefix `MY_CUSTOM_PREFIX` to all of the symbols listed in
+`/path/to/symbols.txt`.
+
+It is currently the caller's responsibility to create and maintain the list of
+symbols to be prefixed. Alternatively, `util/read_symbols.go` reads the list of
+exported symbols from a `.a` file, and can be used in a build script to generate
+the symbol list on the fly (by building without prefixing, using
+`read_symbols.go` to construct a symbol list, and then building again with
+prefixing).
+
+This mechanism is under development and may change over time. Please contact the
+BoringSSL maintainers if making use of it.
+
 ## Known Limitations on Windows
 
   * Versions of CMake since 3.0.2 have a bug in its Ninja generator that causes
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b7e032b..1586d34 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -20,6 +20,11 @@
 enable_language(C)
 enable_language(CXX)
 
+# This is a dummy target which all other targets depend on (manually - see other
+# CMakeLists.txt files). This gives us a hook to add any targets which need to
+# run before all other targets.
+add_custom_target(global_target)
+
 if(ANDROID)
   # Android-NDK CMake files reconfigure the path and so Go and Perl won't be
   # found. However, ninja will still find them in $PATH if we just name them.
@@ -41,10 +46,37 @@
 if(USE_CUSTOM_LIBCXX)
   set(BORINGSSL_ALLOW_CXX_RUNTIME 1)
 endif()
+
 if(BORINGSSL_ALLOW_CXX_RUNTIME)
   add_definitions(-DBORINGSSL_ALLOW_CXX_RUNTIME)
 endif()
 
+if(BORINGSSL_PREFIX AND BORINGSSL_PREFIX_SYMBOLS)
+  add_definitions(-DBORINGSSL_PREFIX=${BORINGSSL_PREFIX})
+
+  # Use "symbol_prefix_include" to store generated header files
+  include_directories(${CMAKE_CURRENT_BINARY_DIR}/symbol_prefix_include)
+  add_custom_command(
+    OUTPUT symbol_prefix_include/boringssl_prefix_symbols.h
+           symbol_prefix_include/boringssl_prefix_symbols_asm.h
+           symbol_prefix_include/boringssl_prefix_symbols_nasm.inc
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/symbol_prefix_include
+    COMMAND ${GO_EXECUTABLE} run ${CMAKE_CURRENT_SOURCE_DIR}/util/make_prefix_headers.go -out ${CMAKE_CURRENT_BINARY_DIR}/symbol_prefix_include ${BORINGSSL_PREFIX_SYMBOLS}
+    DEPENDS util/make_prefix_headers.go
+            ${CMAKE_BINARY_DIR}/${BORINGSSL_PREFIX_SYMBOLS})
+
+  # add_dependencies needs a target, not a file, so we add an intermediate
+  # target.
+  add_custom_target(
+    boringssl_prefix_symbols
+    DEPENDS symbol_prefix_include/boringssl_prefix_symbols.h
+            symbol_prefix_include/boringssl_prefix_symbols_asm.h
+            symbol_prefix_include/boringssl_prefix_symbols_nasm.inc)
+  add_dependencies(global_target boringssl_prefix_symbols)
+elseif(BORINGSSL_PREFIX OR BORINGSSL_PREFIX_SYMBOLS)
+  message(FATAL_ERROR "Must specify both or neither of BORINGSSL_PREFIX and BORINGSSL_PREFIX_SYMBOLS")
+endif()
+
 if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
   set(CLANG 1)
 endif()
@@ -206,6 +238,10 @@
     message(FATAL_ERROR "You need to build with Clang for fuzzing to work")
   endif()
 
+  if(CMAKE_C_COMPILER_VERSION VERSION_LESS "6.0.0")
+    message(FATAL_ERROR "You need Clang ≥ 6.0.0")
+  endif()
+
   add_definitions(-DBORINGSSL_UNSAFE_DETERMINISTIC_MODE)
   set(RUNNER_ARGS "-deterministic")
 
@@ -214,10 +250,8 @@
     set(RUNNER_ARGS ${RUNNER_ARGS} "-fuzzer" "-shim-config" "fuzzer_mode.json")
   endif()
 
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fsanitize-coverage=edge,indirect-calls,trace-pc-guard")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize-coverage=edge,indirect-calls,trace-pc-guard")
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address")
-  link_directories(.)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address,fuzzer-no-link -fsanitize-coverage=edge,indirect-calls")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address,fuzzer-no-link -fsanitize-coverage=edge,indirect-calls")
 endif()
 
 add_definitions(-DBORINGSSL_IMPLEMENTATION)
@@ -300,6 +334,43 @@
   add_definitions(-DOPENSSL_SMALL)
 endif()
 
+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")
+    # 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.
+    execute_process(COMMAND ${GO_EXECUTABLE} run ${godeps} -format cmake
+                            -pkg ${package}
+                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+                    OUTPUT_VARIABLE sources
+                    RESULT_VARIABLE godeps_result)
+    add_custom_command(OUTPUT ${dest}
+                       COMMAND ${GO_EXECUTABLE} build
+                               -o ${CMAKE_CURRENT_BINARY_DIR}/${dest} ${package}
+                       WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+                       DEPENDS ${sources} ${CMAKE_SOURCE_DIR}/go.mod)
+  else()
+    # Ninja expects the target in the depfile to match the output. This is a
+    # relative path from the build directory.
+    string(LENGTH "${CMAKE_BINARY_DIR}" root_dir_length)
+    math(EXPR root_dir_length "${root_dir_length} + 1")
+    string(SUBSTRING "${CMAKE_CURRENT_BINARY_DIR}" ${root_dir_length} -1 target)
+    set(target "${target}/${dest}")
+
+    set(depfile "${CMAKE_CURRENT_BINARY_DIR}/${dest}.d")
+    add_custom_command(OUTPUT ${dest}
+                       COMMAND ${GO_EXECUTABLE} build
+                               -o ${CMAKE_CURRENT_BINARY_DIR}/${dest} ${package}
+                       COMMAND ${GO_EXECUTABLE} run ${godeps} -format depfile
+                               -target ${target} -pkg ${package} -out ${depfile}
+                       WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+                       DEPENDS ${godeps} ${CMAKE_SOURCE_DIR}/go.mod
+                       DEPFILE ${depfile})
+  endif()
+endfunction()
+
 # CMake's iOS support uses Apple's multiple-architecture toolchain. It takes an
 # architecture list from CMAKE_OSX_ARCHITECTURES, leaves CMAKE_SYSTEM_PROCESSOR
 # alone, and expects all architecture-specific logic to be conditioned within
@@ -439,7 +510,6 @@
 add_library(crypto_test_data OBJECT crypto_test_data.cc)
 
 add_subdirectory(crypto)
-add_subdirectory(third_party/fiat)
 add_subdirectory(ssl)
 add_subdirectory(ssl/test)
 add_subdirectory(fipstools)
@@ -447,14 +517,6 @@
 add_subdirectory(decrepit)
 
 if(FUZZ)
-  if(LIBFUZZER_FROM_DEPS)
-    file(GLOB LIBFUZZER_SOURCES "util/bot/libFuzzer/*.cpp")
-    add_library(Fuzzer STATIC ${LIBFUZZER_SOURCES})
-    # libFuzzer does not pass our aggressive warnings. It also must be built
-    # without -fsanitize-coverage options or clang crashes.
-    set_target_properties(Fuzzer PROPERTIES COMPILE_FLAGS "-Wno-shadow -Wno-format-nonliteral -Wno-missing-prototypes -fsanitize-coverage=0")
-  endif()
-
   add_subdirectory(fuzz)
 endif()
 
diff --git a/src/FUZZING.md b/src/FUZZING.md
index 60457c6..200f7a5 100644
--- a/src/FUZZING.md
+++ b/src/FUZZING.md
@@ -2,23 +2,17 @@
 
 Modern fuzz testers are very effective and we wish to use them to ensure that no silly bugs creep into BoringSSL.
 
-We primarily use Clang's [libFuzzer](http://llvm.org/docs/LibFuzzer.html) for fuzz testing and there are a number of fuzz testing functions in `fuzz/`. They are not built by default because they require libFuzzer at build time.
+We use Clang's [libFuzzer](http://llvm.org/docs/LibFuzzer.html) for fuzz testing and there are a number of fuzz testing functions in `fuzz/`. They are not built by default because they require that the rest of BoringSSL be built with some changes that make fuzzing much more effective, but are completely unsafe for real use.
 
-In order to build the fuzz tests you will need at least Clang 3.7. Pass `-DFUZZ=1` on the CMake command line to enable building BoringSSL with coverage and AddressSanitizer, and to build the fuzz test binaries. You'll probably need to set the `CC` and `CXX` environment variables too, like this:
+In order to build the fuzz tests you will need at least Clang 6.0. Pass `-DFUZZ=1` on the CMake command line to enable building BoringSSL with coverage and AddressSanitizer, and to build the fuzz test binaries. You'll probably need to set the `CC` and `CXX` environment variables too, like this:
 
 ```
+mkdir build
+cd build
 CC=clang CXX=clang++ cmake -GNinja -DFUZZ=1 ..
+ninja
 ```
 
-In order for the fuzz tests to link, the linker needs to find libFuzzer. This is not commonly provided and you may need to download the [Clang source code](http://llvm.org/releases/download.html) and do the following:
-
-```
-svn co http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer
-clang++ -c -g -O2 -std=c++11 Fuzzer/*.cpp -IFuzzer
-ar ruv libFuzzer.a Fuzzer*.o
-```
-
-Then copy `libFuzzer.a` to the top-level of your BoringSSL source directory.
 
 From the `build/` directory, you can then run the fuzzers. For example:
 
@@ -32,6 +26,7 @@
 
 | Test          | `max_len` value |
 |---------------|-----------------|
+| `bn_mod_exp`  | 4096            |
 | `cert`        | 10000           |
 | `client`      | 20000           |
 | `pkcs8`       | 2048            |
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 2684750..ee9626a 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -62,8 +62,14 @@
 endif()
 
 function(perlasm dest src)
+  get_filename_component(dir ${dest} DIRECTORY)
+  if ("${dir}" STREQUAL "")
+    set(dir ".")
+  endif()
+
   add_custom_command(
     OUTPUT ${dest}
+    COMMAND ${CMAKE_COMMAND} -E make_directory ${dir}
     COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${src} ${PERLASM_STYLE} ${PERLASM_FLAGS} ${ARGN} ${dest}
     DEPENDS
     ${src}
@@ -78,78 +84,8 @@
   )
 endfunction()
 
-# Level 0.1 - depends on nothing outside this set.
-add_subdirectory(stack)
-add_subdirectory(lhash)
-add_subdirectory(err)
-add_subdirectory(buf)
-add_subdirectory(base64)
-add_subdirectory(bytestring)
-add_subdirectory(pool)
-
-# Level 0.2 - depends on nothing but itself
-add_subdirectory(rc4)
-add_subdirectory(conf)
-add_subdirectory(chacha)
-add_subdirectory(poly1305)
-add_subdirectory(curve25519)
-
-# Level 1, depends only on 0.*
-add_subdirectory(digest_extra)
-add_subdirectory(cipher_extra)
-add_subdirectory(rand_extra)
-add_subdirectory(bio)
-add_subdirectory(bn_extra)
-add_subdirectory(obj)
-add_subdirectory(asn1)
-
-# Level 2
-add_subdirectory(engine)
-add_subdirectory(dh)
-add_subdirectory(dsa)
-add_subdirectory(rsa_extra)
-add_subdirectory(ec_extra)
-add_subdirectory(ecdh_extra)
-add_subdirectory(ecdsa_extra)
-
-# Level 3
-add_subdirectory(cmac)
-add_subdirectory(evp)
-add_subdirectory(hkdf)
-add_subdirectory(pem)
-add_subdirectory(x509)
-add_subdirectory(x509v3)
-
-# Level 4
-add_subdirectory(pkcs7)
-add_subdirectory(pkcs8)
-
-# Test support code
-add_subdirectory(test)
-
 add_subdirectory(fipsmodule)
-
-add_library(
-  crypto_base
-
-  OBJECT
-
-  cpu-aarch64-fuchsia.c
-  cpu-aarch64-linux.c
-  cpu-arm.c
-  cpu-arm-linux.c
-  cpu-intel.c
-  cpu-ppc64le.c
-  crypto.c
-  ex_data.c
-  mem.c
-  refcount_c11.c
-  refcount_lock.c
-  thread.c
-  thread_none.c
-  thread_pthread.c
-  thread_win.c
-)
+add_subdirectory(test)
 
 if(FIPS_DELOCATE)
   SET_SOURCE_FILES_PROPERTIES(fipsmodule/bcm.o PROPERTIES EXTERNAL_OBJECT true)
@@ -162,50 +98,311 @@
   )
 endif()
 
+if(${ARCH} STREQUAL "arm")
+  set(
+    CRYPTO_ARCH_SOURCES
+
+    chacha/chacha-armv4.${ASM_EXT}
+    curve25519/asm/x25519-asm-arm.S
+    poly1305/poly1305_arm_asm.S
+  )
+endif()
+
+if(${ARCH} STREQUAL "aarch64")
+  set(
+    CRYPTO_ARCH_SOURCES
+
+    chacha/chacha-armv8.${ASM_EXT}
+  )
+endif()
+
+if(${ARCH} STREQUAL "x86")
+  set(
+    CRYPTO_ARCH_SOURCES
+
+    chacha/chacha-x86.${ASM_EXT}
+  )
+endif()
+
+if(${ARCH} STREQUAL "x86_64")
+  set(
+    CRYPTO_ARCH_SOURCES
+
+    chacha/chacha-x86_64.${ASM_EXT}
+    cipher_extra/aes128gcmsiv-x86_64.${ASM_EXT}
+    cipher_extra/chacha20_poly1305_x86_64.${ASM_EXT}
+  )
+endif()
+
+perlasm(chacha/chacha-armv4.${ASM_EXT} chacha/asm/chacha-armv4.pl)
+perlasm(chacha/chacha-armv8.${ASM_EXT} chacha/asm/chacha-armv8.pl)
+perlasm(chacha/chacha-x86.${ASM_EXT} chacha/asm/chacha-x86.pl)
+perlasm(chacha/chacha-x86_64.${ASM_EXT} chacha/asm/chacha-x86_64.pl)
+perlasm(cipher_extra/aes128gcmsiv-x86_64.${ASM_EXT} cipher_extra/asm/aes128gcmsiv-x86_64.pl)
+perlasm(cipher_extra/chacha20_poly1305_x86_64.${ASM_EXT} cipher_extra/asm/chacha20_poly1305_x86_64.pl)
+
+add_custom_command(
+  OUTPUT err_data.c
+  COMMAND ${GO_EXECUTABLE} run err_data_generate.go > ${CMAKE_CURRENT_BINARY_DIR}/err_data.c
+  DEPENDS
+  err/err_data_generate.go
+  err/asn1.errordata
+  err/bio.errordata
+  err/bn.errordata
+  err/cipher.errordata
+  err/conf.errordata
+  err/dh.errordata
+  err/digest.errordata
+  err/dsa.errordata
+  err/ecdh.errordata
+  err/ecdsa.errordata
+  err/ec.errordata
+  err/engine.errordata
+  err/evp.errordata
+  err/hkdf.errordata
+  err/obj.errordata
+  err/pem.errordata
+  err/pkcs7.errordata
+  err/pkcs8.errordata
+  err/rsa.errordata
+  err/ssl.errordata
+  err/x509.errordata
+  err/x509v3.errordata
+  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/err
+)
+
 add_library(
   crypto
 
-  $<TARGET_OBJECTS:crypto_base>
-  $<TARGET_OBJECTS:stack>
-  $<TARGET_OBJECTS:lhash>
-  $<TARGET_OBJECTS:err>
-  $<TARGET_OBJECTS:base64>
-  $<TARGET_OBJECTS:bytestring>
-  $<TARGET_OBJECTS:pool>
-  $<TARGET_OBJECTS:fipsmodule>
-  $<TARGET_OBJECTS:digest_extra>
-  $<TARGET_OBJECTS:cipher_extra>
-  $<TARGET_OBJECTS:rc4>
-  $<TARGET_OBJECTS:conf>
-  $<TARGET_OBJECTS:chacha>
-  $<TARGET_OBJECTS:poly1305>
-  $<TARGET_OBJECTS:curve25519>
-  $<TARGET_OBJECTS:fiat>
-  $<TARGET_OBJECTS:buf>
-  $<TARGET_OBJECTS:bn_extra>
-  $<TARGET_OBJECTS:bio>
-  $<TARGET_OBJECTS:rand_extra>
-  $<TARGET_OBJECTS:obj>
-  $<TARGET_OBJECTS:asn1>
-  $<TARGET_OBJECTS:engine>
-  $<TARGET_OBJECTS:dh>
-  $<TARGET_OBJECTS:dsa>
-  $<TARGET_OBJECTS:rsa_extra>
-  $<TARGET_OBJECTS:ec_extra>
-  $<TARGET_OBJECTS:ecdh_extra>
-  $<TARGET_OBJECTS:ecdsa_extra>
-  $<TARGET_OBJECTS:cmac>
-  $<TARGET_OBJECTS:evp>
-  $<TARGET_OBJECTS:hkdf>
-  $<TARGET_OBJECTS:pem>
-  $<TARGET_OBJECTS:x509>
-  $<TARGET_OBJECTS:x509v3>
-  $<TARGET_OBJECTS:pkcs7>
-  $<TARGET_OBJECTS:pkcs8_lib>
+  asn1/a_bitstr.c
+  asn1/a_bool.c
+  asn1/a_d2i_fp.c
+  asn1/a_dup.c
+  asn1/a_enum.c
+  asn1/a_gentm.c
+  asn1/a_i2d_fp.c
+  asn1/a_int.c
+  asn1/a_mbstr.c
+  asn1/a_object.c
+  asn1/a_octet.c
+  asn1/a_print.c
+  asn1/a_strnid.c
+  asn1/a_time.c
+  asn1/a_type.c
+  asn1/a_utctm.c
+  asn1/a_utf8.c
+  asn1/asn1_lib.c
+  asn1/asn1_par.c
+  asn1/asn_pack.c
+  asn1/f_enum.c
+  asn1/f_int.c
+  asn1/f_string.c
+  asn1/tasn_dec.c
+  asn1/tasn_enc.c
+  asn1/tasn_fre.c
+  asn1/tasn_new.c
+  asn1/tasn_typ.c
+  asn1/tasn_utl.c
+  asn1/time_support.c
+  base64/base64.c
+  bio/bio.c
+  bio/bio_mem.c
+  bio/connect.c
+  bio/fd.c
+  bio/file.c
+  bio/hexdump.c
+  bio/pair.c
+  bio/printf.c
+  bio/socket.c
+  bio/socket_helper.c
+  bn_extra/bn_asn1.c
+  bn_extra/convert.c
+  buf/buf.c
+  bytestring/asn1_compat.c
+  bytestring/ber.c
+  bytestring/cbb.c
+  bytestring/cbs.c
+  bytestring/unicode.c
+  chacha/chacha.c
+  cipher_extra/cipher_extra.c
+  cipher_extra/derive_key.c
+  cipher_extra/e_aesccm.c
+  cipher_extra/e_aesctrhmac.c
+  cipher_extra/e_aesgcmsiv.c
+  cipher_extra/e_chacha20poly1305.c
+  cipher_extra/e_null.c
+  cipher_extra/e_rc2.c
+  cipher_extra/e_rc4.c
+  cipher_extra/e_tls.c
+  cipher_extra/tls_cbc.c
+  cmac/cmac.c
+  conf/conf.c
+  cpu-aarch64-fuchsia.c
+  cpu-aarch64-linux.c
+  cpu-arm-linux.c
+  cpu-arm.c
+  cpu-intel.c
+  cpu-ppc64le.c
+  crypto.c
+  curve25519/spake25519.c
+  dh/dh.c
+  dh/params.c
+  dh/check.c
+  dh/dh_asn1.c
+  digest_extra/digest_extra.c
+  dsa/dsa.c
+  dsa/dsa_asn1.c
+  ecdh_extra/ecdh_extra.c
+  ecdsa_extra/ecdsa_asn1.c
+  ec_extra/ec_asn1.c
+  err/err.c
+  err_data.c
+  engine/engine.c
+  evp/digestsign.c
+  evp/evp.c
+  evp/evp_asn1.c
+  evp/evp_ctx.c
+  evp/p_dsa_asn1.c
+  evp/p_ec.c
+  evp/p_ec_asn1.c
+  evp/p_ed25519.c
+  evp/p_ed25519_asn1.c
+  evp/p_rsa.c
+  evp/p_rsa_asn1.c
+  evp/pbkdf.c
+  evp/print.c
+  evp/scrypt.c
+  evp/sign.c
+  ex_data.c
+  hkdf/hkdf.c
+  lhash/lhash.c
+  mem.c
+  obj/obj.c
+  obj/obj_xref.c
+  pem/pem_all.c
+  pem/pem_info.c
+  pem/pem_lib.c
+  pem/pem_oth.c
+  pem/pem_pk8.c
+  pem/pem_pkey.c
+  pem/pem_x509.c
+  pem/pem_xaux.c
+  pkcs7/pkcs7.c
+  pkcs7/pkcs7_x509.c
+  pkcs8/pkcs8.c
+  pkcs8/pkcs8_x509.c
+  pkcs8/p5_pbev2.c
+  poly1305/poly1305.c
+  poly1305/poly1305_arm.c
+  poly1305/poly1305_vec.c
+  pool/pool.c
+  rand_extra/deterministic.c
+  rand_extra/forkunsafe.c
+  rand_extra/fuchsia.c
+  rand_extra/rand_extra.c
+  rand_extra/windows.c
+  rc4/rc4.c
+  refcount_c11.c
+  refcount_lock.c
+  rsa_extra/rsa_asn1.c
+  rsa_extra/rsa_print.c
+  stack/stack.c
+  thread.c
+  thread_none.c
+  thread_pthread.c
+  thread_win.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/rsa_pss.c
+  x509/t_crl.c
+  x509/t_req.c
+  x509/t_x509.c
+  x509/t_x509a.c
+  x509/x509.c
+  x509/x509_att.c
+  x509/x509_cmp.c
+  x509/x509_d2.c
+  x509/x509_def.c
+  x509/x509_ext.c
+  x509/x509_lu.c
+  x509/x509_obj.c
+  x509/x509_r2x.c
+  x509/x509_req.c
+  x509/x509_set.c
+  x509/x509_trs.c
+  x509/x509_txt.c
+  x509/x509_v3.c
+  x509/x509_vfy.c
+  x509/x509_vpm.c
+  x509/x509cset.c
+  x509/x509name.c
+  x509/x509rset.c
+  x509/x509spki.c
+  x509/x_algor.c
+  x509/x_all.c
+  x509/x_attrib.c
+  x509/x_crl.c
+  x509/x_exten.c
+  x509/x_info.c
+  x509/x_name.c
+  x509/x_pkey.c
+  x509/x_pubkey.c
+  x509/x_req.c
+  x509/x_sig.c
+  x509/x_spki.c
+  x509/x_val.c
+  x509/x_x509.c
+  x509/x_x509a.c
+  x509v3/pcy_cache.c
+  x509v3/pcy_data.c
+  x509v3/pcy_lib.c
+  x509v3/pcy_map.c
+  x509v3/pcy_node.c
+  x509v3/pcy_tree.c
+  x509v3/v3_akey.c
+  x509v3/v3_akeya.c
+  x509v3/v3_alt.c
+  x509v3/v3_bcons.c
+  x509v3/v3_bitst.c
+  x509v3/v3_conf.c
+  x509v3/v3_cpols.c
+  x509v3/v3_crld.c
+  x509v3/v3_enum.c
+  x509v3/v3_extku.c
+  x509v3/v3_genn.c
+  x509v3/v3_ia5.c
+  x509v3/v3_info.c
+  x509v3/v3_int.c
+  x509v3/v3_lib.c
+  x509v3/v3_ncons.c
+  x509v3/v3_ocsp.c
+  x509v3/v3_pci.c
+  x509v3/v3_pcia.c
+  x509v3/v3_pcons.c
+  x509v3/v3_pku.c
+  x509v3/v3_pmaps.c
+  x509v3/v3_prn.c
+  x509v3/v3_purp.c
+  x509v3/v3_skey.c
+  x509v3/v3_sxnet.c
+  x509v3/v3_utl.c
+  ../third_party/fiat/curve25519.c
 
+  $<TARGET_OBJECTS:fipsmodule>
+
+  ${CRYPTO_ARCH_SOURCES}
   ${CRYPTO_FIPS_OBJECTS}
 )
 
+add_dependencies(crypto global_target)
+
 if(FIPS_DELOCATE)
   add_dependencies(crypto bcm_o_target)
 endif()
@@ -222,7 +419,6 @@
   target_link_libraries(crypto libcxx)
 endif()
 
-# TODO(davidben): Convert the remaining tests to GTest.
 add_executable(
   crypto_test
 
@@ -237,6 +433,7 @@
   cmac/cmac_test.cc
   compiler_test.cc
   constant_time_test.cc
+  cpu-arm-linux_test.cc
   curve25519/ed25519_test.cc
   curve25519/spake25519_test.cc
   curve25519/x25519_test.cc
@@ -266,9 +463,11 @@
   pkcs8/pkcs12_test.cc
   poly1305/poly1305_test.cc
   pool/pool_test.cc
+  rand_extra/rand_test.cc
   refcount_test.cc
   rsa_extra/rsa_test.cc
   self_test.cc
+  stack/stack_test.cc
   test/file_test_gtest.cc
   thread_test.cc
   x509/x509_test.cc
@@ -281,6 +480,8 @@
   $<TARGET_OBJECTS:test_support>
 )
 
+add_dependencies(crypto_test global_target)
+
 target_link_libraries(crypto_test crypto boringssl_gtest)
 if(WIN32)
   target_link_libraries(crypto_test ws2_32)
diff --git a/src/crypto/asn1/CMakeLists.txt b/src/crypto/asn1/CMakeLists.txt
deleted file mode 100644
index de5d280..0000000
--- a/src/crypto/asn1/CMakeLists.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  asn1
-
-  OBJECT
-
-  a_bitstr.c
-  a_bool.c
-  a_d2i_fp.c
-  a_dup.c
-  a_enum.c
-  a_gentm.c
-  a_i2d_fp.c
-  a_int.c
-  a_mbstr.c
-  a_object.c
-  a_octet.c
-  a_print.c
-  a_strnid.c
-  a_time.c
-  a_type.c
-  a_utctm.c
-  a_utf8.c
-  asn1_lib.c
-  asn1_par.c
-  asn_pack.c
-  f_enum.c
-  f_int.c
-  f_string.c
-  tasn_dec.c
-  tasn_enc.c
-  tasn_fre.c
-  tasn_new.c
-  tasn_typ.c
-  tasn_utl.c
-  time_support.c
-)
diff --git a/src/crypto/asn1/a_d2i_fp.c b/src/crypto/asn1/a_d2i_fp.c
index 3da6df9..fd423e2 100644
--- a/src/crypto/asn1/a_d2i_fp.c
+++ b/src/crypto/asn1/a_d2i_fp.c
@@ -58,240 +58,36 @@
 
 #include <limits.h>
 
-#include <openssl/buf.h>
+#include <openssl/bio.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb);
-
-#ifndef NO_OLD_ASN1
-# ifndef OPENSSL_NO_FP_API
-
-void *ASN1_d2i_fp(void *(*xnew) (void), d2i_of_void *d2i, FILE *in, void **x)
-{
-    BIO *b;
-    void *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (NULL);
-    }
-    BIO_set_fp(b, in, BIO_NOCLOSE);
-    ret = ASN1_d2i_bio(xnew, d2i, b, x);
-    BIO_free(b);
-    return (ret);
-}
-# endif
-
-void *ASN1_d2i_bio(void *(*xnew) (void), d2i_of_void *d2i, BIO *in, void **x)
-{
-    BUF_MEM *b = NULL;
-    const unsigned char *p;
-    void *ret = NULL;
-    int len;
-
-    len = asn1_d2i_read_bio(in, &b);
-    if (len < 0)
-        goto err;
-
-    p = (unsigned char *)b->data;
-    ret = d2i(x, &p, len);
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return (ret);
-}
-
-#endif
 
 void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x)
 {
-    BUF_MEM *b = NULL;
-    const unsigned char *p;
-    void *ret = NULL;
-    int len;
-
-    len = asn1_d2i_read_bio(in, &b);
-    if (len < 0)
-        goto err;
-
-    p = (const unsigned char *)b->data;
-    ret = ASN1_item_d2i(x, &p, len, it);
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return (ret);
+    uint8_t *data;
+    size_t len;
+    // Historically, this function did not impose a limit in OpenSSL and is used
+    // to read CRLs, so we leave this without an external bound.
+    if (!BIO_read_asn1(in, &data, &len, INT_MAX)) {
+        return NULL;
+    }
+    const uint8_t *ptr = data;
+    void *ret = ASN1_item_d2i(x, &ptr, len, it);
+    OPENSSL_free(data);
+    return ret;
 }
 
 #ifndef OPENSSL_NO_FP_API
 void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)
 {
-    BIO *b;
-    char *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(in, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (NULL);
+        return NULL;
     }
-    BIO_set_fp(b, in, BIO_NOCLOSE);
-    ret = ASN1_item_d2i_bio(it, b, x);
+    void *ret = ASN1_item_d2i_bio(it, b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
-
-typedef struct asn1_const_ctx_st
-    {
-    const unsigned char *p;/* work char pointer */
-    int eos;    /* end of sequence read for indefinite encoding */
-    int error;  /* error code to use when returning an error */
-    int inf;    /* constructed if 0x20, indefinite is 0x21 */
-    int tag;    /* tag from last 'get object' */
-    int xclass; /* class from last 'get object' */
-    long slen;  /* length of last 'get object' */
-    const unsigned char *max; /* largest value of p allowed */
-    const unsigned char *q;/* temporary variable */
-    const unsigned char **pp;/* variable */
-    int line;   /* used in error processing */
-    } ASN1_const_CTX;
-
-#define HEADER_SIZE   8
-#define ASN1_CHUNK_INITIAL_SIZE (16 * 1024)
-static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
-{
-    BUF_MEM *b;
-    unsigned char *p;
-    int i;
-    ASN1_const_CTX c;
-    size_t want = HEADER_SIZE;
-    int eos = 0;
-    size_t off = 0;
-    size_t len = 0;
-
-    b = BUF_MEM_new();
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return -1;
-    }
-
-    ERR_clear_error();
-    for (;;) {
-        if (want >= (len - off)) {
-            want -= (len - off);
-
-            if (len + want < len || !BUF_MEM_grow_clean(b, len + want)) {
-                OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-                goto err;
-            }
-            i = BIO_read(in, &(b->data[len]), want);
-            if ((i < 0) && ((len - off) == 0)) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
-                goto err;
-            }
-            if (i > 0) {
-                if (len + i < len) {
-                    OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                    goto err;
-                }
-                len += i;
-            }
-        }
-        /* else data already loaded */
-
-        p = (unsigned char *)&(b->data[off]);
-        c.p = p;
-        c.inf = ASN1_get_object(&(c.p), &(c.slen), &(c.tag), &(c.xclass),
-                                len - off);
-        if (c.inf & 0x80) {
-            uint32_t e;
-
-            e = ERR_GET_REASON(ERR_peek_error());
-            if (e != ASN1_R_TOO_LONG)
-                goto err;
-            else
-                ERR_clear_error(); /* clear error */
-        }
-        i = c.p - p;            /* header length */
-        off += i;               /* end of data */
-
-        if (c.inf & 1) {
-            /* no data body so go round again */
-            eos++;
-            if (eos < 0) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_HEADER_TOO_LONG);
-                goto err;
-            }
-            want = HEADER_SIZE;
-        } else if (eos && (c.slen == 0) && (c.tag == V_ASN1_EOC)) {
-            /* eos value, so go back and read another header */
-            eos--;
-            if (eos <= 0)
-                break;
-            else
-                want = HEADER_SIZE;
-        } else {
-            /* suck in c.slen bytes of data */
-            want = c.slen;
-            if (want > (len - off)) {
-                size_t chunk_max = ASN1_CHUNK_INITIAL_SIZE;
-                want -= (len - off);
-                if (want > INT_MAX /* BIO_read takes an int length */  ||
-                    len + want < len) {
-                    OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                    goto err;
-                }
-                while (want > 0) {
-                    /*
-                     * Read content in chunks of increasing size
-                     * so we can return an error for EOF without
-                     * having to allocate the entire content length
-                     * in one go.
-                     */
-                    size_t chunk = want > chunk_max ? chunk_max : want;
-
-                    if (!BUF_MEM_grow_clean(b, len + chunk)) {
-                        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-                        goto err;
-                    }
-                    want -= chunk;
-                    while (chunk > 0) {
-                        i = BIO_read(in, &(b->data[len]), chunk);
-                        if (i <= 0) {
-                            OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
-                            goto err;
-                        }
-                        /*
-                         * This can't overflow because |len+want| didn't
-                         * overflow.
-                         */
-                        len += i;
-                        chunk -= i;
-                    }
-                    if (chunk_max < INT_MAX/2)
-                        chunk_max *= 2;
-                }
-            }
-            if (off + c.slen < off) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                goto err;
-            }
-            off += c.slen;
-            if (eos <= 0) {
-                break;
-            } else
-                want = HEADER_SIZE;
-        }
-    }
-
-    if (off > INT_MAX) {
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-        goto err;
-    }
-
-    *pb = b;
-    return off;
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return -1;
-}
diff --git a/src/crypto/asn1/a_dup.c b/src/crypto/asn1/a_dup.c
index 57394f5..9ede851 100644
--- a/src/crypto/asn1/a_dup.c
+++ b/src/crypto/asn1/a_dup.c
@@ -59,30 +59,6 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *x)
-{
-    unsigned char *b, *p;
-    const unsigned char *p2;
-    int i;
-    char *ret;
-
-    if (x == NULL)
-        return (NULL);
-
-    i = i2d(x, NULL);
-    b = OPENSSL_malloc(i + 10);
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (NULL);
-    }
-    p = b;
-    i = i2d(x, &p);
-    p2 = b;
-    ret = d2i(NULL, &p2, i);
-    OPENSSL_free(b);
-    return (ret);
-}
-
 /*
  * ASN1_ITEM version of dup: this follows the model above except we don't
  * need to allocate the buffer. At some point this could be rewritten to
diff --git a/src/crypto/asn1/a_i2d_fp.c b/src/crypto/asn1/a_i2d_fp.c
index 7b76d0c..db0d812 100644
--- a/src/crypto/asn1/a_i2d_fp.c
+++ b/src/crypto/asn1/a_i2d_fp.c
@@ -56,95 +56,33 @@
 
 #include <openssl/asn1.h>
 
+#include <openssl/bio.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-int ASN1_i2d_fp(i2d_of_void *i2d, FILE *out, void *x)
-{
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (0);
-    }
-    BIO_set_fp(b, out, BIO_NOCLOSE);
-    ret = ASN1_i2d_bio(i2d, b, x);
-    BIO_free(b);
-    return (ret);
-}
-
-int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, void *x)
-{
-    char *b;
-    unsigned char *p;
-    int i, j = 0, n, ret = 1;
-
-    n = i2d(x, NULL);
-    if (n <= 0)
-        return 0;
-
-    b = (char *)OPENSSL_malloc(n);
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (0);
-    }
-
-    p = (unsigned char *)b;
-    i2d(x, &p);
-
-    for (;;) {
-        i = BIO_write(out, &(b[j]), n);
-        if (i == n)
-            break;
-        if (i <= 0) {
-            ret = 0;
-            break;
-        }
-        j += i;
-        n -= i;
-    }
-    OPENSSL_free(b);
-    return (ret);
-}
 
 int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(out, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, out, BIO_NOCLOSE);
-    ret = ASN1_item_i2d_bio(it, b, x);
+    int ret = ASN1_item_i2d_bio(it, b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x)
 {
     unsigned char *b = NULL;
-    int i, j = 0, n, ret = 1;
-
-    n = ASN1_item_i2d(x, &b, it);
+    int n = ASN1_item_i2d(x, &b, it);
     if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (0);
+        return 0;
     }
 
-    for (;;) {
-        i = BIO_write(out, &(b[j]), n);
-        if (i == n)
-            break;
-        if (i <= 0) {
-            ret = 0;
-            break;
-        }
-        j += i;
-        n -= i;
-    }
+    int ret = BIO_write_all(out, b, n);
     OPENSSL_free(b);
-    return (ret);
+    return ret;
 }
diff --git a/src/crypto/base64/CMakeLists.txt b/src/crypto/base64/CMakeLists.txt
deleted file mode 100644
index 18cf9fe..0000000
--- a/src/crypto/base64/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  base64
-
-  OBJECT
-
-  base64.c
-)
diff --git a/src/crypto/bio/CMakeLists.txt b/src/crypto/bio/CMakeLists.txt
deleted file mode 100644
index fccb152..0000000
--- a/src/crypto/bio/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  bio
-
-  OBJECT
-
-  bio.c
-  bio_mem.c
-  connect.c
-  fd.c
-  file.c
-  hexdump.c
-  pair.c
-  printf.c
-  socket.c
-  socket_helper.c
-)
diff --git a/src/crypto/bio/bio.c b/src/crypto/bio/bio.c
index 3e788b8..881c14e 100644
--- a/src/crypto/bio/bio.c
+++ b/src/crypto/bio/bio.c
@@ -177,6 +177,19 @@
   return ret;
 }
 
+int BIO_write_all(BIO *bio, const void *data, size_t len) {
+  const uint8_t *data_u8 = data;
+  while (len > 0) {
+    int ret = BIO_write(bio, data_u8, len > INT_MAX ? INT_MAX : (int)len);
+    if (ret <= 0) {
+      return 0;
+    }
+    data_u8 += ret;
+    len -= ret;
+  }
+  return 1;
+}
+
 int BIO_puts(BIO *bio, const char *in) {
   return BIO_write(bio, in, strlen(in));
 }
diff --git a/src/crypto/bio/file.c b/src/crypto/bio/file.c
index 6a0b9a9..a177763 100644
--- a/src/crypto/bio/file.c
+++ b/src/crypto/bio/file.c
@@ -107,13 +107,12 @@
     return NULL;
   }
 
-  ret = BIO_new(BIO_s_file());
+  ret = BIO_new_fp(file, BIO_CLOSE);
   if (ret == NULL) {
     fclose(file);
     return NULL;
   }
 
-  BIO_set_fp(ret, file, BIO_CLOSE);
   return ret;
 }
 
diff --git a/src/crypto/bn_extra/CMakeLists.txt b/src/crypto/bn_extra/CMakeLists.txt
deleted file mode 100644
index 994b365..0000000
--- a/src/crypto/bn_extra/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  bn_extra
-
-  OBJECT
-
-  bn_asn1.c
-  convert.c
-)
diff --git a/src/crypto/bn_extra/convert.c b/src/crypto/bn_extra/convert.c
index c70ff8b..9a1a69e 100644
--- a/src/crypto/bn_extra/convert.c
+++ b/src/crypto/bn_extra/convert.c
@@ -367,17 +367,13 @@
 }
 
 int BN_print_fp(FILE *fp, const BIGNUM *a) {
-  BIO *b;
-  int ret;
-
-  b = BIO_new(BIO_s_file());
+  BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
   if (b == NULL) {
     return 0;
   }
-  BIO_set_fp(b, fp, BIO_NOCLOSE);
-  ret = BN_print(b, a);
-  BIO_free(b);
 
+  int ret = BN_print(b, a);
+  BIO_free(b);
   return ret;
 }
 
diff --git a/src/crypto/buf/CMakeLists.txt b/src/crypto/buf/CMakeLists.txt
deleted file mode 100644
index 63f1025..0000000
--- a/src/crypto/buf/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  buf
-
-  OBJECT
-
-  buf.c
-)
diff --git a/src/crypto/bytestring/CMakeLists.txt b/src/crypto/bytestring/CMakeLists.txt
deleted file mode 100644
index 2bb4c15..0000000
--- a/src/crypto/bytestring/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  bytestring
-
-  OBJECT
-
-  asn1_compat.c
-  ber.c
-  cbs.c
-  cbb.c
-  unicode.c
-)
diff --git a/src/crypto/chacha/CMakeLists.txt b/src/crypto/chacha/CMakeLists.txt
deleted file mode 100644
index bf4920c..0000000
--- a/src/crypto/chacha/CMakeLists.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-include_directories(../../include)
-
-if(${ARCH} STREQUAL "arm")
-  set(
-    CHACHA_ARCH_SOURCES
-
-    chacha-armv4.${ASM_EXT}
-  )
-endif()
-
-if(${ARCH} STREQUAL "aarch64")
-  set(
-    CHACHA_ARCH_SOURCES
-
-    chacha-armv8.${ASM_EXT}
-  )
-endif()
-
-if(${ARCH} STREQUAL "x86")
-  set(
-    CHACHA_ARCH_SOURCES
-
-    chacha-x86.${ASM_EXT}
-  )
-endif()
-
-if(${ARCH} STREQUAL "x86_64")
-  set(
-    CHACHA_ARCH_SOURCES
-
-    chacha-x86_64.${ASM_EXT}
-  )
-endif()
-
-add_library(
-  chacha
-
-  OBJECT
-
-  chacha.c
-
-  ${CHACHA_ARCH_SOURCES}
-)
-
-perlasm(chacha-armv4.${ASM_EXT} asm/chacha-armv4.pl)
-perlasm(chacha-armv8.${ASM_EXT} asm/chacha-armv8.pl)
-perlasm(chacha-x86.${ASM_EXT} asm/chacha-x86.pl)
-perlasm(chacha-x86_64.${ASM_EXT} asm/chacha-x86_64.pl)
diff --git a/src/crypto/cipher_extra/CMakeLists.txt b/src/crypto/cipher_extra/CMakeLists.txt
deleted file mode 100644
index 2c55bd6..0000000
--- a/src/crypto/cipher_extra/CMakeLists.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-include_directories(../../include)
-
-if(${ARCH} STREQUAL "x86_64")
-  set(
-    CIPHER_ARCH_SOURCES
-
-    aes128gcmsiv-x86_64.${ASM_EXT}
-    chacha20_poly1305_x86_64.${ASM_EXT}
-  )
-endif()
-
-add_library(
-  cipher_extra
-
-  OBJECT
-
-  cipher_extra.c
-  derive_key.c
-
-  e_null.c
-  e_rc2.c
-  e_rc4.c
-  e_aesgcmsiv.c
-  e_aesctrhmac.c
-  e_aesccm.c
-  e_chacha20poly1305.c
-
-  tls_cbc.c
-  e_tls.c
-
-  ${CIPHER_ARCH_SOURCES}
-)
-
-perlasm(aes128gcmsiv-x86_64.${ASM_EXT} asm/aes128gcmsiv-x86_64.pl)
-perlasm(chacha20_poly1305_x86_64.${ASM_EXT} asm/chacha20_poly1305_x86_64.pl)
diff --git a/src/crypto/cmac/CMakeLists.txt b/src/crypto/cmac/CMakeLists.txt
deleted file mode 100644
index 4429903..0000000
--- a/src/crypto/cmac/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  cmac
-
-  OBJECT
-
-  cmac.c
-)
diff --git a/src/crypto/conf/CMakeLists.txt b/src/crypto/conf/CMakeLists.txt
deleted file mode 100644
index 0a3c795..0000000
--- a/src/crypto/conf/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  conf
-
-  OBJECT
-
-  conf.c
-)
diff --git a/src/crypto/conf/conf.c b/src/crypto/conf/conf.c
index b1982f8..4c27ddf 100644
--- a/src/crypto/conf/conf.c
+++ b/src/crypto/conf/conf.c
@@ -62,6 +62,7 @@
 #include <openssl/bio.h>
 #include <openssl/buf.h>
 #include <openssl/err.h>
+#include <openssl/lhash.h>
 #include <openssl/mem.h>
 
 #include "conf_def.h"
@@ -69,6 +70,10 @@
 #include "../internal.h"
 
 
+struct conf_st {
+  LHASH_OF(CONF_VALUE) *data;
+};
+
 // The maximum length we can grow a value to after variable expansion. 64k
 // should be more than enough for all reasonable uses.
 #define MAX_CONF_VALUE_LENGTH 65536
diff --git a/src/crypto/cpu-arm-linux.c b/src/crypto/cpu-arm-linux.c
index 839b632..91078bd 100644
--- a/src/crypto/cpu-arm-linux.c
+++ b/src/crypto/cpu-arm-linux.c
@@ -14,33 +14,171 @@
 
 #include <openssl/cpu.h>
 
-#if defined(OPENSSL_ARM) && !defined(OPENSSL_STATIC_ARMCAP)
+#include "cpu-arm-linux.h"
+#include "internal.h"
 
+#if defined(OPENSSL_ARM) && !defined(OPENSSL_STATIC_ARMCAP)
 #include <errno.h>
 #include <fcntl.h>
-#include <string.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <openssl/arm_arch.h>
 #include <openssl/buf.h>
 #include <openssl/mem.h>
+#endif
 
-#include "internal.h"
 
+// The following functions are only used in ARM, but they are defined on all
+// platforms for testing and fuzzing purposes.
+
+static int STRING_PIECE_equals(const STRING_PIECE *a, const char *b) {
+  size_t b_len = strlen(b);
+  return a->len == b_len && OPENSSL_memcmp(a->data, b, b_len) == 0;
+}
+
+// STRING_PIECE_split finds the first occurence of |sep| in |in| and, if found,
+// sets |*out_left| and |*out_right| to |in| split before and after it. It
+// returns one if |sep| was found and zero otherwise.
+static int STRING_PIECE_split(STRING_PIECE *out_left, STRING_PIECE *out_right,
+                              const STRING_PIECE *in, char sep) {
+  const char *p = OPENSSL_memchr(in->data, sep, in->len);
+  if (p == NULL) {
+    return 0;
+  }
+  // |out_left| or |out_right| may alias |in|, so make a copy.
+  STRING_PIECE in_copy = *in;
+  out_left->data = in_copy.data;
+  out_left->len = p - in_copy.data;
+  out_right->data = in_copy.data + out_left->len + 1;
+  out_right->len = in_copy.len - out_left->len - 1;
+  return 1;
+}
+
+// STRING_PIECE_get_delimited reads a |sep|-delimited entry from |s|, writing it
+// to |out| and updating |s| to point beyond it. It returns one on success and
+// zero if |s| is empty. If |s| is has no copies of |sep| and is non-empty, it
+// reads the entire string to |out|.
+static int STRING_PIECE_get_delimited(STRING_PIECE *s, STRING_PIECE *out, char sep) {
+  if (s->len == 0) {
+    return 0;
+  }
+  if (!STRING_PIECE_split(out, s, s, sep)) {
+    // |s| had no instances of |sep|. Return the entire string.
+    *out = *s;
+    s->data += s->len;
+    s->len = 0;
+  }
+  return 1;
+}
+
+// STRING_PIECE_trim removes leading and trailing whitespace from |s|.
+static void STRING_PIECE_trim(STRING_PIECE *s) {
+  while (s->len != 0 && (s->data[0] == ' ' || s->data[0] == '\t')) {
+    s->data++;
+    s->len--;
+  }
+  while (s->len != 0 &&
+         (s->data[s->len - 1] == ' ' || s->data[s->len - 1] == '\t')) {
+    s->len--;
+  }
+}
+
+// extract_cpuinfo_field extracts a /proc/cpuinfo field named |field| from
+// |in|. If found, it sets |*out| to the value and returns one. Otherwise, it
+// returns zero.
+static int extract_cpuinfo_field(STRING_PIECE *out, const STRING_PIECE *in,
+                                 const char *field) {
+  // Process |in| one line at a time.
+  STRING_PIECE remaining = *in, line;
+  while (STRING_PIECE_get_delimited(&remaining, &line, '\n')) {
+    STRING_PIECE key, value;
+    if (!STRING_PIECE_split(&key, &value, &line, ':')) {
+      continue;
+    }
+    STRING_PIECE_trim(&key);
+    if (STRING_PIECE_equals(&key, field)) {
+      STRING_PIECE_trim(&value);
+      *out = value;
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static int cpuinfo_field_equals(const STRING_PIECE *cpuinfo, const char *field,
+                                const char *value) {
+  STRING_PIECE extracted;
+  return extract_cpuinfo_field(&extracted, cpuinfo, field) &&
+         STRING_PIECE_equals(&extracted, value);
+}
+
+// has_list_item treats |list| as a space-separated list of items and returns
+// one if |item| is contained in |list| and zero otherwise.
+static int has_list_item(const STRING_PIECE *list, const char *item) {
+  STRING_PIECE remaining = *list, feature;
+  while (STRING_PIECE_get_delimited(&remaining, &feature, ' ')) {
+    if (STRING_PIECE_equals(&feature, item)) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+unsigned long crypto_get_arm_hwcap_from_cpuinfo(const STRING_PIECE *cpuinfo) {
+  if (cpuinfo_field_equals(cpuinfo, "CPU architecture", "8")) {
+    // This is a 32-bit ARM binary running on a 64-bit kernel. NEON is always
+    // available on ARMv8. Linux omits required features, so reading the
+    // "Features" line does not work. (For simplicity, use strict equality. We
+    // assume everything running on future ARM architectures will have a
+    // working |getauxval|.)
+    return HWCAP_NEON;
+  }
+
+  STRING_PIECE features;
+  if (extract_cpuinfo_field(&features, cpuinfo, "Features") &&
+      has_list_item(&features, "neon")) {
+    return HWCAP_NEON;
+  }
+  return 0;
+}
+
+unsigned long crypto_get_arm_hwcap2_from_cpuinfo(const STRING_PIECE *cpuinfo) {
+  STRING_PIECE features;
+  if (!extract_cpuinfo_field(&features, cpuinfo, "Features")) {
+    return 0;
+  }
+
+  unsigned long ret = 0;
+  if (has_list_item(&features, "aes")) {
+    ret |= HWCAP2_AES;
+  }
+  if (has_list_item(&features, "pmull")) {
+    ret |= HWCAP2_PMULL;
+  }
+  if (has_list_item(&features, "sha1")) {
+    ret |= HWCAP2_SHA1;
+  }
+  if (has_list_item(&features, "sha2")) {
+    ret |= HWCAP2_SHA2;
+  }
+  return ret;
+}
+
+int crypto_cpuinfo_has_broken_neon(const STRING_PIECE *cpuinfo) {
+  return cpuinfo_field_equals(cpuinfo, "CPU implementer", "0x51") &&
+         cpuinfo_field_equals(cpuinfo, "CPU architecture", "7") &&
+         cpuinfo_field_equals(cpuinfo, "CPU variant", "0x1") &&
+         cpuinfo_field_equals(cpuinfo, "CPU part", "0x04d") &&
+         cpuinfo_field_equals(cpuinfo, "CPU revision", "0");
+}
+
+#if defined(OPENSSL_ARM) && !defined(OPENSSL_STATIC_ARMCAP)
 
 #define AT_HWCAP 16
 #define AT_HWCAP2 26
 
-#define HWCAP_NEON (1 << 12)
-
-// See /usr/include/asm/hwcap.h on an ARM installation for the source of
-// these values.
-#define HWCAP2_AES (1 << 0)
-#define HWCAP2_PMULL (1 << 1)
-#define HWCAP2_SHA1 (1 << 2)
-#define HWCAP2_SHA2 (1 << 3)
-
 // |getauxval| is not available on Android until API level 20. Link it as a weak
 // symbol and use other methods as fallback.
 unsigned long getauxval(unsigned long type) __attribute__((weak));
@@ -154,138 +292,6 @@
   return 0;
 }
 
-typedef struct {
-  const char *data;
-  size_t len;
-} STRING_PIECE;
-
-static int STRING_PIECE_equals(const STRING_PIECE *a, const char *b) {
-  size_t b_len = strlen(b);
-  return a->len == b_len && OPENSSL_memcmp(a->data, b, b_len) == 0;
-}
-
-// STRING_PIECE_split finds the first occurence of |sep| in |in| and, if found,
-// sets |*out_left| and |*out_right| to |in| split before and after it. It
-// returns one if |sep| was found and zero otherwise.
-static int STRING_PIECE_split(STRING_PIECE *out_left, STRING_PIECE *out_right,
-                              const STRING_PIECE *in, char sep) {
-  const char *p = OPENSSL_memchr(in->data, sep, in->len);
-  if (p == NULL) {
-    return 0;
-  }
-  // |out_left| or |out_right| may alias |in|, so make a copy.
-  STRING_PIECE in_copy = *in;
-  out_left->data = in_copy.data;
-  out_left->len = p - in_copy.data;
-  out_right->data = in_copy.data + out_left->len + 1;
-  out_right->len = in_copy.len - out_left->len - 1;
-  return 1;
-}
-
-// STRING_PIECE_trim removes leading and trailing whitespace from |s|.
-static void STRING_PIECE_trim(STRING_PIECE *s) {
-  while (s->len != 0 && (s->data[0] == ' ' || s->data[0] == '\t')) {
-    s->data++;
-    s->len--;
-  }
-  while (s->len != 0 &&
-         (s->data[s->len - 1] == ' ' || s->data[s->len - 1] == '\t')) {
-    s->len--;
-  }
-}
-
-// extract_cpuinfo_field extracts a /proc/cpuinfo field named |field| from
-// |in|.  If found, it sets |*out| to the value and returns one. Otherwise, it
-// returns zero.
-static int extract_cpuinfo_field(STRING_PIECE *out, const STRING_PIECE *in,
-                                 const char *field) {
-  // Process |in| one line at a time.
-  STRING_PIECE remaining = *in, line;
-  while (STRING_PIECE_split(&line, &remaining, &remaining, '\n')) {
-    STRING_PIECE key, value;
-    if (!STRING_PIECE_split(&key, &value, &line, ':')) {
-      continue;
-    }
-    STRING_PIECE_trim(&key);
-    if (STRING_PIECE_equals(&key, field)) {
-      STRING_PIECE_trim(&value);
-      *out = value;
-      return 1;
-    }
-  }
-
-  return 0;
-}
-
-static int cpuinfo_field_equals(const STRING_PIECE *cpuinfo, const char *field,
-                                const char *value) {
-  STRING_PIECE extracted;
-  return extract_cpuinfo_field(&extracted, cpuinfo, field) &&
-         STRING_PIECE_equals(&extracted, value);
-}
-
-// has_list_item treats |list| as a space-separated list of items and returns
-// one if |item| is contained in |list| and zero otherwise.
-static int has_list_item(const STRING_PIECE *list, const char *item) {
-  STRING_PIECE remaining = *list, feature;
-  while (STRING_PIECE_split(&feature, &remaining, &remaining, ' ')) {
-    if (STRING_PIECE_equals(&feature, item)) {
-      return 1;
-    }
-  }
-  return 0;
-}
-
-static unsigned long get_hwcap_cpuinfo(const STRING_PIECE *cpuinfo) {
-  if (cpuinfo_field_equals(cpuinfo, "CPU architecture", "8")) {
-    // This is a 32-bit ARM binary running on a 64-bit kernel. NEON is always
-    // available on ARMv8. Linux omits required features, so reading the
-    // "Features" line does not work. (For simplicity, use strict equality. We
-    // assume everything running on future ARM architectures will have a
-    // working |getauxval|.)
-    return HWCAP_NEON;
-  }
-
-  STRING_PIECE features;
-  if (extract_cpuinfo_field(&features, cpuinfo, "Features") &&
-      has_list_item(&features, "neon")) {
-    return HWCAP_NEON;
-  }
-  return 0;
-}
-
-static unsigned long get_hwcap2_cpuinfo(const STRING_PIECE *cpuinfo) {
-  STRING_PIECE features;
-  if (!extract_cpuinfo_field(&features, cpuinfo, "Features")) {
-    return 0;
-  }
-
-  unsigned long ret = 0;
-  if (has_list_item(&features, "aes")) {
-    ret |= HWCAP2_AES;
-  }
-  if (has_list_item(&features, "pmull")) {
-    ret |= HWCAP2_PMULL;
-  }
-  if (has_list_item(&features, "sha1")) {
-    ret |= HWCAP2_SHA1;
-  }
-  if (has_list_item(&features, "sha2")) {
-    ret |= HWCAP2_SHA2;
-  }
-  return ret;
-}
-
-// has_broken_neon returns one if |in| matches a CPU known to have a broken
-// NEON unit. See https://crbug.com/341598.
-static int has_broken_neon(const STRING_PIECE *cpuinfo) {
-  return cpuinfo_field_equals(cpuinfo, "CPU implementer", "0x51") &&
-         cpuinfo_field_equals(cpuinfo, "CPU architecture", "7") &&
-         cpuinfo_field_equals(cpuinfo, "CPU variant", "0x1") &&
-         cpuinfo_field_equals(cpuinfo, "CPU part", "0x04d") &&
-         cpuinfo_field_equals(cpuinfo, "CPU revision", "0");
-}
-
 extern uint32_t OPENSSL_armcap_P;
 
 static int g_has_broken_neon, g_needs_hwcap2_workaround;
@@ -315,11 +321,11 @@
     hwcap = getauxval_proc(AT_HWCAP);
   }
   if (hwcap == 0) {
-    hwcap = get_hwcap_cpuinfo(&cpuinfo);
+    hwcap = crypto_get_arm_hwcap_from_cpuinfo(&cpuinfo);
   }
 
   // Clear NEON support if known broken.
-  g_has_broken_neon = has_broken_neon(&cpuinfo);
+  g_has_broken_neon = crypto_cpuinfo_has_broken_neon(&cpuinfo);
   if (g_has_broken_neon) {
     hwcap &= ~HWCAP_NEON;
   }
@@ -335,7 +341,7 @@
       hwcap2 = getauxval(AT_HWCAP2);
     }
     if (hwcap2 == 0) {
-      hwcap2 = get_hwcap2_cpuinfo(&cpuinfo);
+      hwcap2 = crypto_get_arm_hwcap2_from_cpuinfo(&cpuinfo);
       g_needs_hwcap2_workaround = hwcap2 != 0;
     }
 
diff --git a/src/crypto/cpu-arm-linux.h b/src/crypto/cpu-arm-linux.h
new file mode 100644
index 0000000..eabf4ea
--- /dev/null
+++ b/src/crypto/cpu-arm-linux.h
@@ -0,0 +1,59 @@
+/* Copyright (c) 2018, 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. */
+
+#ifndef OPENSSL_HEADER_CRYPTO_CPU_ARM_LINUX_H
+#define OPENSSL_HEADER_CRYPTO_CPU_ARM_LINUX_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// The following symbols are defined on all platforms and exported for testing
+// and fuzzing purposes. They are not exported from the shared library so the
+// static linker will drop them outside of tests.
+
+#define HWCAP_NEON (1 << 12)
+
+// See /usr/include/asm/hwcap.h on an ARM installation for the source of
+// these values.
+#define HWCAP2_AES (1 << 0)
+#define HWCAP2_PMULL (1 << 1)
+#define HWCAP2_SHA1 (1 << 2)
+#define HWCAP2_SHA2 (1 << 3)
+
+typedef struct {
+  const char *data;
+  size_t len;
+} STRING_PIECE;
+
+// crypto_get_arm_hwcap_from_cpuinfo returns an equivalent ARM |AT_HWCAP| value
+// from |cpuinfo|.
+unsigned long crypto_get_arm_hwcap_from_cpuinfo(const STRING_PIECE *cpuinfo);
+
+// crypto_get_arm_hwcap2_from_cpuinfo returns an equivalent ARM |AT_HWCAP2|
+// value from |cpuinfo|.
+unsigned long crypto_get_arm_hwcap2_from_cpuinfo(const STRING_PIECE *cpuinfo);
+
+// crypto_cpuinfo_has_broken_neon returns one if |cpuinfo| matches a CPU known
+// to have broken NEON unit and zero otherwise. See https://crbug.com/341598.
+int crypto_cpuinfo_has_broken_neon(const STRING_PIECE *cpuinfo);
+
+#if defined(__cplusplus)
+}  // extern C
+#endif
+
+#endif  // OPENSSL_HEADER_CRYPTO_CPU_ARM_LINUX_H
diff --git a/src/crypto/cpu-arm-linux_test.cc b/src/crypto/cpu-arm-linux_test.cc
new file mode 100644
index 0000000..3ca6e57
--- /dev/null
+++ b/src/crypto/cpu-arm-linux_test.cc
@@ -0,0 +1,236 @@
+/* Copyright (c) 2018, 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. */
+
+#include "cpu-arm-linux.h"
+
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+
+#if !defined(BORINGSSL_SHARED_LIBRARY)
+
+TEST(ARMLinuxTest, CPUInfo) {
+  struct CPUInfoTest {
+    const char *cpuinfo;
+    unsigned long hwcap;
+    unsigned long hwcap2;
+    bool broken_neon;
+  } kTests[] = {
+      // https://crbug.com/341598#c33
+      {
+          "Processor: ARMv7 Processory rev 0 (v71)\n"
+          "processor: 0\n"
+          "BogoMIPS: 13.50\n"
+          "\n"
+          "Processor: 1\n"
+          "BogoMIPS: 13.50\n"
+          "\n"
+          "Features: swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 "
+          "idiva idivt\n"
+          "CPU implementer : 0x51\n"
+          "CPU architecture: 7\n"
+          "CPU variant: 0x1\n"
+          "CPU part: 0x04d\n"
+          "CPU revision: 0\n"
+          "\n"
+          "Hardware: SAMSUNG M2\n"
+          "Revision: 0010\n"
+          "Serial: 00001e030000354e\n",
+          HWCAP_NEON,
+          0,
+          true,
+      },
+      // https://crbug.com/341598#c39
+      {
+          "Processor       : ARMv7 Processor rev 0 (v7l)\n"
+          "processor       : 0\n"
+          "BogoMIPS        : 13.53\n"
+          "\n"
+          "Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls "
+          "vfpv4\n"
+          "CPU implementer : 0x51\n"
+          "CPU architecture: 7\n"
+          "CPU variant     : 0x1\n"
+          "CPU part        : 0x04d\n"
+          "CPU revision    : 0\n"
+          "\n"
+          "Hardware        : SAMSUNG M2_ATT\n"
+          "Revision        : 0010\n"
+          "Serial          : 0000df0c00004d4c\n",
+          HWCAP_NEON,
+          0,
+          true,
+      },
+      // Nexus 4 from https://crbug.com/341598#c43
+      {
+          "Processor       : ARMv7 Processor rev 2 (v7l)\n"
+          "processor       : 0\n"
+          "BogoMIPS        : 13.53\n"
+          "\n"
+          "processor       : 1\n"
+          "BogoMIPS        : 13.53\n"
+          "\n"
+          "processor       : 2\n"
+          "BogoMIPS        : 13.53\n"
+          "\n"
+          "processor       : 3\n"
+          "BogoMIPS        : 13.53\n"
+          "\n"
+          "Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls "
+          "vfpv4 \n"
+          "CPU implementer : 0x51\n"
+          "CPU architecture: 7\n"
+          "CPU variant     : 0x0\n"
+          "CPU part        : 0x06f\n"
+          "CPU revision    : 2\n"
+          "\n"
+          "Hardware        : QCT APQ8064 MAKO\n"
+          "Revision        : 000b\n"
+          "Serial          : 0000000000000000\n",
+          HWCAP_NEON,
+          0,
+          false,
+      },
+      // Razr M from https://crbug.com/341598#c43
+      {
+          "Processor       : ARMv7 Processor rev 4 (v7l)\n"
+          "processor       : 0\n"
+          "BogoMIPS        : 13.53\n"
+          "\n"
+          "Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls "
+          "vfpv4\n"
+          "CPU implementer : 0x51\n"
+          "CPU architecture: 7\n"
+          "CPU variant     : 0x1\n"
+          "CPU part        : 0x04d\n"
+          "CPU revision    : 4\n"
+          "\n"
+          "Hardware        : msm8960dt\n"
+          "Revision        : 82a0\n"
+          "Serial          : 0001000201fe37a5\n",
+          HWCAP_NEON,
+          0,
+          false,
+      },
+      // Pixel 2 (truncated slightly)
+      {
+          "Processor       : AArch64 Processor rev 1 (aarch64)\n"
+          "processor       : 0\n"
+          "BogoMIPS        : 38.00\n"
+          "Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32\n"
+          "CPU implementer : 0x51\n"
+          "CPU architecture: 8\n"
+          "CPU variant     : 0xa\n"
+          "CPU part        : 0x801\n"
+          "CPU revision    : 4\n"
+          "\n"
+          "processor       : 1\n"
+          "BogoMIPS        : 38.00\n"
+          "Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32\n"
+          "CPU implementer : 0x51\n"
+          "CPU architecture: 8\n"
+          "CPU variant     : 0xa\n"
+          "CPU part        : 0x801\n"
+          "CPU revision    : 4\n"
+          "\n"
+          "processor       : 2\n"
+          "BogoMIPS        : 38.00\n"
+          "Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32\n"
+          "CPU implementer : 0x51\n"
+          "CPU architecture: 8\n"
+          "CPU variant     : 0xa\n"
+          "CPU part        : 0x801\n"
+          "CPU revision    : 4\n"
+          "\n"
+          "processor       : 3\n"
+          "BogoMIPS        : 38.00\n"
+          "Features        : fp asimd evtstrm aes pmull sha1 sha2 crc32\n"
+          "CPU implementer : 0x51\n"
+          "CPU architecture: 8\n"
+          "CPU variant     : 0xa\n"
+          "CPU part        : 0x801\n"
+          "CPU revision    : 4\n"
+          // (Extra processors omitted.)
+          "\n"
+          "Hardware        : Qualcomm Technologies, Inc MSM8998\n",
+          HWCAP_NEON,  // CPU architecture 8 implies NEON.
+          HWCAP2_AES | HWCAP2_PMULL | HWCAP2_SHA1 | HWCAP2_SHA2,
+          false,
+      },
+      // Nexus 4 from
+      // Garbage should be tolerated.
+      {
+          "Blah blah blah this is definitely an ARM CPU",
+          0,
+          0,
+          false,
+      },
+      // A hypothetical ARMv8 CPU without crc32 (and thus no trailing space
+      // after the last crypto entry).
+      {
+          "Features        : aes pmull sha1 sha2\n"
+          "CPU architecture: 8\n",
+          HWCAP_NEON,
+          HWCAP2_AES | HWCAP2_PMULL | HWCAP2_SHA1 | HWCAP2_SHA2,
+          false,
+      },
+      // Various combinations of ARMv8 flags.
+      {
+          "Features        : aes sha1 sha2\n"
+          "CPU architecture: 8\n",
+          HWCAP_NEON,
+          HWCAP2_AES | HWCAP2_SHA1 | HWCAP2_SHA2,
+          false,
+      },
+      {
+          "Features        : pmull sha2\n"
+          "CPU architecture: 8\n",
+          HWCAP_NEON,
+          HWCAP2_PMULL | HWCAP2_SHA2,
+          false,
+      },
+      {
+          "Features        : aes aes   aes not_aes aes aes \n"
+          "CPU architecture: 8\n",
+          HWCAP_NEON,
+          HWCAP2_AES,
+          false,
+      },
+      {
+          "Features        : \n"
+          "CPU architecture: 8\n",
+          HWCAP_NEON,
+          0,
+          false,
+      },
+      {
+          "Features        : nothing\n"
+          "CPU architecture: 8\n",
+          HWCAP_NEON,
+          0,
+          false,
+      },
+  };
+
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.cpuinfo);
+    STRING_PIECE sp = {t.cpuinfo, strlen(t.cpuinfo)};
+    EXPECT_EQ(t.hwcap, crypto_get_arm_hwcap_from_cpuinfo(&sp));
+    EXPECT_EQ(t.hwcap2, crypto_get_arm_hwcap2_from_cpuinfo(&sp));
+    EXPECT_EQ(t.broken_neon ? 1 : 0, crypto_cpuinfo_has_broken_neon(&sp));
+  }
+}
+
+#endif  // !BORINGSSL_SHARED_LIBRARY
diff --git a/src/crypto/curve25519/CMakeLists.txt b/src/crypto/curve25519/CMakeLists.txt
deleted file mode 100644
index 0f28218..0000000
--- a/src/crypto/curve25519/CMakeLists.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-include_directories(../../include)
-
-if(${ARCH} STREQUAL "arm")
-  set(
-    CURVE25519_ARCH_SOURCES
-
-    asm/x25519-asm-arm.S
-  )
-endif()
-
-add_library(
-  curve25519
-
-  OBJECT
-
-  spake25519.c
-
-  ${CURVE25519_ARCH_SOURCES}
-)
diff --git a/src/crypto/curve25519/asm/x25519-asm-arm.S b/src/crypto/curve25519/asm/x25519-asm-arm.S
index 89d98ae..905af07 100644
--- a/src/crypto/curve25519/asm/x25519-asm-arm.S
+++ b/src/crypto/curve25519/asm/x25519-asm-arm.S
@@ -25,6 +25,10 @@
 
 #if !defined(OPENSSL_NO_ASM) && defined(__arm__) && !defined(__APPLE__)
 
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
+
 .fpu neon
 .text
 .align 4
diff --git a/src/crypto/dh/CMakeLists.txt b/src/crypto/dh/CMakeLists.txt
deleted file mode 100644
index 83ae6d4..0000000
--- a/src/crypto/dh/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  dh
-
-  OBJECT
-
-  dh.c
-  params.c
-  check.c
-  dh_asn1.c
-)
diff --git a/src/crypto/digest_extra/CMakeLists.txt b/src/crypto/digest_extra/CMakeLists.txt
deleted file mode 100644
index 2efb832..0000000
--- a/src/crypto/digest_extra/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  digest_extra
-
-  OBJECT
-
-  digest_extra.c
-)
diff --git a/src/crypto/dsa/CMakeLists.txt b/src/crypto/dsa/CMakeLists.txt
deleted file mode 100644
index d3c12f5..0000000
--- a/src/crypto/dsa/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  dsa
-
-  OBJECT
-
-  dsa.c
-  dsa_asn1.c
-)
diff --git a/src/crypto/ec_extra/CMakeLists.txt b/src/crypto/ec_extra/CMakeLists.txt
deleted file mode 100644
index 2312b19..0000000
--- a/src/crypto/ec_extra/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  ec_extra
-
-  OBJECT
-
-  ec_asn1.c
-)
diff --git a/src/crypto/ecdh_extra/CMakeLists.txt b/src/crypto/ecdh_extra/CMakeLists.txt
deleted file mode 100644
index 40a53c1..0000000
--- a/src/crypto/ecdh_extra/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  ecdh_extra
-
-  OBJECT
-
-  ecdh_extra.c
-)
diff --git a/src/crypto/ecdsa_extra/CMakeLists.txt b/src/crypto/ecdsa_extra/CMakeLists.txt
deleted file mode 100644
index a908580..0000000
--- a/src/crypto/ecdsa_extra/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  ecdsa_extra
-
-  OBJECT
-
-  ecdsa_asn1.c
-)
diff --git a/src/crypto/engine/CMakeLists.txt b/src/crypto/engine/CMakeLists.txt
deleted file mode 100644
index 5667f02..0000000
--- a/src/crypto/engine/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  engine
-
-  OBJECT
-
-  engine.c
-)
diff --git a/src/crypto/err/CMakeLists.txt b/src/crypto/err/CMakeLists.txt
deleted file mode 100644
index 91c6f6e..0000000
--- a/src/crypto/err/CMakeLists.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-include_directories(../../include)
-
-add_custom_command(
-  OUTPUT err_data.c
-  COMMAND ${GO_EXECUTABLE} run err_data_generate.go > ${CMAKE_CURRENT_BINARY_DIR}/err_data.c
-  DEPENDS
-  err_data_generate.go
-  asn1.errordata
-  bio.errordata
-  bn.errordata
-  cipher.errordata
-  conf.errordata
-  dh.errordata
-  digest.errordata
-  dsa.errordata
-  ecdh.errordata
-  ecdsa.errordata
-  ec.errordata
-  engine.errordata
-  evp.errordata
-  hkdf.errordata
-  obj.errordata
-  pem.errordata
-  pkcs7.errordata
-  pkcs8.errordata
-  rsa.errordata
-  ssl.errordata
-  x509.errordata
-  x509v3.errordata
-  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-)
-
-add_library(
-  err
-
-  OBJECT
-
-  err.c
-  err_data.c
-)
diff --git a/src/crypto/err/internal.h b/src/crypto/err/internal.h
index 3f2397c..179f756 100644
--- a/src/crypto/err/internal.h
+++ b/src/crypto/err/internal.h
@@ -46,11 +46,11 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(ERR_SAVE_STATE, ERR_SAVE_STATE_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 #endif
diff --git a/src/crypto/err/x509.errordata b/src/crypto/err/x509.errordata
index 1a8c4f1..6ed8fa3 100644
--- a/src/crypto/err/x509.errordata
+++ b/src/crypto/err/x509.errordata
@@ -28,6 +28,7 @@
 X509,125,PUBLIC_KEY_DECODE_ERROR
 X509,126,PUBLIC_KEY_ENCODE_ERROR
 X509,127,SHOULD_RETRY
+X509,137,SIGNATURE_ALGORITHM_MISMATCH
 X509,128,UNKNOWN_KEY_TYPE
 X509,129,UNKNOWN_NID
 X509,130,UNKNOWN_PURPOSE_ID
diff --git a/src/crypto/evp/CMakeLists.txt b/src/crypto/evp/CMakeLists.txt
deleted file mode 100644
index 4b1fe5e..0000000
--- a/src/crypto/evp/CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  evp
-
-  OBJECT
-
-  digestsign.c
-  evp.c
-  evp_asn1.c
-  evp_ctx.c
-  p_dsa_asn1.c
-  p_ec.c
-  p_ec_asn1.c
-  p_ed25519.c
-  p_ed25519_asn1.c
-  p_rsa.c
-  p_rsa_asn1.c
-  pbkdf.c
-  print.c
-  scrypt.c
-  sign.c
-)
diff --git a/src/crypto/evp/evp.c b/src/crypto/evp/evp.c
index 4feadb7..ed7cc85 100644
--- a/src/crypto/evp/evp.c
+++ b/src/crypto/evp/evp.c
@@ -176,7 +176,7 @@
   return 0;
 }
 
-int EVP_PKEY_bits(EVP_PKEY *pkey) {
+int EVP_PKEY_bits(const EVP_PKEY *pkey) {
   if (pkey && pkey->ameth && pkey->ameth->pkey_bits) {
     return pkey->ameth->pkey_bits(pkey);
   }
@@ -225,7 +225,7 @@
   return EVP_PKEY_assign(pkey, EVP_PKEY_RSA, key);
 }
 
-RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) {
+RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey) {
   if (pkey->type != EVP_PKEY_RSA) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_EXPECTING_AN_RSA_KEY);
     return NULL;
@@ -233,7 +233,7 @@
   return pkey->pkey.rsa;
 }
 
-RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey) {
+RSA *EVP_PKEY_get1_RSA(const EVP_PKEY *pkey) {
   RSA *rsa = EVP_PKEY_get0_RSA(pkey);
   if (rsa != NULL) {
     RSA_up_ref(rsa);
@@ -253,7 +253,7 @@
   return EVP_PKEY_assign(pkey, EVP_PKEY_DSA, key);
 }
 
-DSA *EVP_PKEY_get0_DSA(EVP_PKEY *pkey) {
+DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey) {
   if (pkey->type != EVP_PKEY_DSA) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_EXPECTING_A_DSA_KEY);
     return NULL;
@@ -261,7 +261,7 @@
   return pkey->pkey.dsa;
 }
 
-DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey) {
+DSA *EVP_PKEY_get1_DSA(const EVP_PKEY *pkey) {
   DSA *dsa = EVP_PKEY_get0_DSA(pkey);
   if (dsa != NULL) {
     DSA_up_ref(dsa);
@@ -281,7 +281,7 @@
   return EVP_PKEY_assign(pkey, EVP_PKEY_EC, key);
 }
 
-EC_KEY *EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey) {
+EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey) {
   if (pkey->type != EVP_PKEY_EC) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_EXPECTING_AN_EC_KEY_KEY);
     return NULL;
@@ -289,7 +289,7 @@
   return pkey->pkey.ec;
 }
 
-EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey) {
+EC_KEY *EVP_PKEY_get1_EC_KEY(const EVP_PKEY *pkey) {
   EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
   if (ec_key != NULL) {
     EC_KEY_up_ref(ec_key);
@@ -297,8 +297,8 @@
   return ec_key;
 }
 
-DH *EVP_PKEY_get0_DH(EVP_PKEY *pkey) { return NULL; }
-DH *EVP_PKEY_get1_DH(EVP_PKEY *pkey) { return NULL; }
+DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey) { return NULL; }
+DH *EVP_PKEY_get1_DH(const EVP_PKEY *pkey) { return NULL; }
 
 int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key) {
   if (!EVP_PKEY_set_type(pkey, type)) {
diff --git a/src/crypto/evp/evp_asn1.c b/src/crypto/evp/evp_asn1.c
index 81c7a71..383e2f9 100644
--- a/src/crypto/evp/evp_asn1.c
+++ b/src/crypto/evp/evp_asn1.c
@@ -331,7 +331,7 @@
   }
 }
 
-int i2d_PublicKey(EVP_PKEY *key, uint8_t **outp) {
+int i2d_PublicKey(const EVP_PKEY *key, uint8_t **outp) {
   switch (key->type) {
     case EVP_PKEY_RSA:
       return i2d_RSAPublicKey(key->pkey.rsa, outp);
diff --git a/src/crypto/fipsmodule/CMakeLists.txt b/src/crypto/fipsmodule/CMakeLists.txt
index babda94..e6c8cc6 100644
--- a/src/crypto/fipsmodule/CMakeLists.txt
+++ b/src/crypto/fipsmodule/CMakeLists.txt
@@ -136,24 +136,17 @@
     bcm.c
   )
 
+  add_dependencies(bcm_c_generated_asm global_target)
+
   set_target_properties(bcm_c_generated_asm PROPERTIES COMPILE_OPTIONS "-S")
   set_target_properties(bcm_c_generated_asm PROPERTIES POSITION_INDEPENDENT_CODE ON)
 
-  function(prepend_path values prefix output)
-    set(result)
-    foreach(value ${values})
-      list(APPEND result "${prefix}/${value}")
-    endforeach(value)
-    set(${output} ${result} PARENT_SCOPE)
-  endfunction()
-
-  prepend_path("${BCM_ASM_SOURCES}" "${CMAKE_CURRENT_BINARY_DIR}" DELOCATE_ASM_ARGS)
-
+  go_executable(delocate boringssl.googlesource.com/boringssl/util/fipstools/delocate)
   add_custom_command(
     OUTPUT bcm-delocated.S
-    COMMAND ${GO_EXECUTABLE} run util/fipstools/delocate.go util/fipstools/delocate.peg.go util/fipstools/ar.go util/fipstools/const.go -a $<TARGET_FILE:bcm_c_generated_asm> -o ${CMAKE_CURRENT_BINARY_DIR}/bcm-delocated.S ${DELOCATE_ASM_ARGS}
-    DEPENDS bcm_c_generated_asm ${BCM_ASM_SOURCES} ${CMAKE_SOURCE_DIR}/util/fipstools/delocate.go ${CMAKE_SOURCE_DIR}/util/fipstools/delocate.peg.go ${CMAKE_SOURCE_DIR}/util/fipstools/ar.go ${CMAKE_SOURCE_DIR}/util/fipstools/const.go
-    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    COMMAND ./delocate -a $<TARGET_FILE:bcm_c_generated_asm> -o bcm-delocated.S ${BCM_ASM_SOURCES}
+    DEPENDS bcm_c_generated_asm delocate ${BCM_ASM_SOURCES}
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
   )
 
   add_library(
@@ -164,14 +157,18 @@
     bcm-delocated.S
   )
 
+  add_dependencies(bcm_hashunset global_target)
+
   set_target_properties(bcm_hashunset PROPERTIES POSITION_INDEPENDENT_CODE ON)
   set_target_properties(bcm_hashunset PROPERTIES LINKER_LANGUAGE C)
 
+  go_executable(inject_hash
+	        boringssl.googlesource.com/boringssl/util/fipstools/inject_hash)
   add_custom_command(
     OUTPUT bcm.o
-    COMMAND ${GO_EXECUTABLE} run util/fipstools/inject-hash.go util/fipstools/ar.go util/fipstools/const.go -o ${CMAKE_CURRENT_BINARY_DIR}/bcm.o -in-archive $<TARGET_FILE:bcm_hashunset>
-    DEPENDS bcm_hashunset ${CMAKE_SOURCE_DIR}/util/fipstools/inject-hash.go ${CMAKE_SOURCE_DIR}/util/fipstools/ar.go ${CMAKE_SOURCE_DIR}/util/fipstools/const.go
-    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+    COMMAND ./inject_hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset>
+    DEPENDS bcm_hashunset inject_hash
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
   )
 
   # The outputs of add_custom_command cannot be referenced outside of the
@@ -187,6 +184,8 @@
     is_fips.c
   )
 
+  add_dependencies(fipsmodule global_target)
+
   set_target_properties(fipsmodule PROPERTIES LINKER_LANGUAGE C)
 else()
   add_library(
@@ -199,4 +198,6 @@
 
     ${BCM_ASM_SOURCES}
   )
+
+  add_dependencies(fipsmodule global_target)
 endif()
diff --git a/src/crypto/fipsmodule/FIPS.md b/src/crypto/fipsmodule/FIPS.md
index a691d18..426b38e 100644
--- a/src/crypto/fipsmodule/FIPS.md
+++ b/src/crypto/fipsmodule/FIPS.md
@@ -103,7 +103,7 @@
 
 In order to actually implement the integrity test, a constructor function within the module calculates an HMAC from `module_start` to `module_end` using a fixed, all-zero key. It compares the result with the known-good value added (by the script) to the unhashed portion of the text segment. If they don't match, it calls `exit` in an infinite loop.
 
-Initially the known-good value will be incorrect. Another script (`inject-hash.go`) calculates the correct value from the assembled object and injects it back into the object.
+Initially the known-good value will be incorrect. Another script (`inject_hash.go`) calculates the correct value from the assembled object and injects it back into the object.
 
 ![build process](/crypto/fipsmodule/intcheck2.png)
 
diff --git a/src/crypto/fipsmodule/aes/mode_wrappers.c b/src/crypto/fipsmodule/aes/mode_wrappers.c
index 5c50c85..0140c70 100644
--- a/src/crypto/fipsmodule/aes/mode_wrappers.c
+++ b/src/crypto/fipsmodule/aes/mode_wrappers.c
@@ -57,8 +57,7 @@
 void AES_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
                         const AES_KEY *key, uint8_t ivec[AES_BLOCK_SIZE],
                         uint8_t ecount_buf[AES_BLOCK_SIZE], unsigned int *num) {
-  CRYPTO_ctr128_encrypt(in, out, len, key, ivec, ecount_buf, num,
-                        (block128_f)AES_encrypt);
+  CRYPTO_ctr128_encrypt(in, out, len, key, ivec, ecount_buf, num, AES_encrypt);
 }
 
 void AES_ecb_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key,
@@ -90,9 +89,9 @@
   aes_nohw_cbc_encrypt(in, out, len, key, ivec, enc);
 #else
   if (enc) {
-    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, (block128_f)AES_encrypt);
+    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, AES_encrypt);
   } else {
-    CRYPTO_cbc128_decrypt(in, out, len, key, ivec, (block128_f)AES_decrypt);
+    CRYPTO_cbc128_decrypt(in, out, len, key, ivec, AES_decrypt);
   }
 #endif
 }
@@ -100,8 +99,7 @@
 void AES_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t length,
                         const AES_KEY *key, uint8_t *ivec, int *num) {
   unsigned num_u = (unsigned)(*num);
-  CRYPTO_ofb128_encrypt(in, out, length, key, ivec, &num_u,
-                        (block128_f)AES_encrypt);
+  CRYPTO_ofb128_encrypt(in, out, length, key, ivec, &num_u, AES_encrypt);
   *num = (int)num_u;
 }
 
@@ -109,7 +107,6 @@
                         const AES_KEY *key, uint8_t *ivec, int *num,
                         int enc) {
   unsigned num_u = (unsigned)(*num);
-  CRYPTO_cfb128_encrypt(in, out, length, key, ivec, &num_u, enc,
-                        (block128_f)AES_encrypt);
+  CRYPTO_cfb128_encrypt(in, out, length, key, ivec, &num_u, enc, AES_encrypt);
   *num = (int)num_u;
 }
diff --git a/src/crypto/fipsmodule/bcm.c b/src/crypto/fipsmodule/bcm.c
index 6b21f06..e15ecb8 100644
--- a/src/crypto/fipsmodule/bcm.c
+++ b/src/crypto/fipsmodule/bcm.c
@@ -124,6 +124,7 @@
   if (!HMAC(EVP_sha512(), kHMACKey, sizeof(kHMACKey), start, end - start,
             result, &result_len) ||
       result_len != sizeof(result)) {
+    fprintf(stderr, "HMAC failed.\n");
     goto err;
   }
 
diff --git a/src/crypto/fipsmodule/bn/bn_test.cc b/src/crypto/fipsmodule/bn/bn_test.cc
index a932306..29b4456 100644
--- a/src/crypto/fipsmodule/bn/bn_test.cc
+++ b/src/crypto/fipsmodule/bn/bn_test.cc
@@ -1592,21 +1592,6 @@
   ASSERT_TRUE(BN_mod_exp_mont_consttime(r.get(), zero.get(), zero.get(),
                                         BN_value_one(), ctx(), nullptr));
   EXPECT_TRUE(BN_is_zero(r.get()));
-
-  // Historically, OpenSSL's modular exponentiation functions tolerated negative
-  // moduli by ignoring the sign bit. This logic should do the same.
-  ASSERT_TRUE(BN_mod_exp(r.get(), a.get(), zero.get(), minus_one.get(), ctx()));
-  EXPECT_TRUE(BN_is_zero(r.get()));
-  ASSERT_TRUE(BN_mod_exp_mont_word(r.get(), 0, zero.get(), minus_one.get(),
-                                   ctx(), nullptr));
-  EXPECT_TRUE(BN_is_zero(r.get()));
-  ASSERT_TRUE(BN_mod_exp_mont(r.get(), zero.get(), zero.get(), minus_one.get(),
-                              ctx(), nullptr));
-  EXPECT_TRUE(BN_is_zero(r.get()));
-
-  ASSERT_TRUE(BN_mod_exp_mont_consttime(r.get(), zero.get(), zero.get(),
-                                        minus_one.get(), ctx(), nullptr));
-  EXPECT_TRUE(BN_is_zero(r.get()));
 }
 
 TEST_F(BNTest, SmallPrime) {
diff --git a/src/crypto/fipsmodule/bn/div.c b/src/crypto/fipsmodule/bn/div.c
index 57485bd..27b591c 100644
--- a/src/crypto/fipsmodule/bn/div.c
+++ b/src/crypto/fipsmodule/bn/div.c
@@ -64,7 +64,7 @@
 #include "internal.h"
 
 
-#if !defined(BN_ULLONG)
+#if !defined(BN_CAN_DIVIDE_ULLONG) && !defined(BN_CAN_USE_INLINE_ASM)
 // bn_div_words divides a double-width |h|,|l| by |d| and returns the result,
 // which must fit in a |BN_ULONG|.
 static BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d) {
@@ -135,7 +135,7 @@
   ret |= q;
   return ret;
 }
-#endif  // !defined(BN_ULLONG)
+#endif  // !defined(BN_CAN_DIVIDE_ULLONG) && !defined(BN_CAN_USE_INLINE_ASM)
 
 static inline void bn_div_rem_words(BN_ULONG *quotient_out, BN_ULONG *rem_out,
                                     BN_ULONG n0, BN_ULONG n1, BN_ULONG d0) {
@@ -155,20 +155,18 @@
   //
   // These issues aren't specific to x86 and x86_64, so it might be worthwhile
   // to add more assembly language implementations.
-#if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86) && \
-    (defined(__GNUC__) || defined(__clang__))
+#if defined(BN_CAN_USE_INLINE_ASM) && defined(OPENSSL_X86)
   __asm__ volatile("divl %4"
                    : "=a"(*quotient_out), "=d"(*rem_out)
                    : "a"(n1), "d"(n0), "rm"(d0)
                    : "cc");
-#elif !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86_64) && \
-    (defined(__GNUC__) || defined(__clang__))
+#elif defined(BN_CAN_USE_INLINE_ASM) && defined(OPENSSL_X86_64)
   __asm__ volatile("divq %4"
                    : "=a"(*quotient_out), "=d"(*rem_out)
                    : "a"(n1), "d"(n0), "rm"(d0)
                    : "cc");
 #else
-#if defined(BN_ULLONG)
+#if defined(BN_CAN_DIVIDE_ULLONG)
   BN_ULLONG n = (((BN_ULLONG)n0) << BN_BITS2) | n1;
   *quotient_out = (BN_ULONG)(n / d0);
 #else
diff --git a/src/crypto/fipsmodule/bn/exponentiation.c b/src/crypto/fipsmodule/bn/exponentiation.c
index 7035ea7..41b2057 100644
--- a/src/crypto/fipsmodule/bn/exponentiation.c
+++ b/src/crypto/fipsmodule/bn/exponentiation.c
@@ -446,21 +446,18 @@
 
 static int mod_exp_recp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
                         const BIGNUM *m, BN_CTX *ctx) {
-  int i, j, bits, ret = 0, wstart, window;
+  int i, j, ret = 0, wstart, window;
   int start = 1;
   BIGNUM *aa;
   // Table of variables obtained from 'ctx'
   BIGNUM *val[TABLE_SIZE];
   BN_RECP_CTX recp;
 
-  bits = BN_num_bits(p);
+  // This function is only called on even moduli.
+  assert(!BN_is_odd(m));
 
+  int bits = BN_num_bits(p);
   if (bits == 0) {
-    // x**0 mod 1 is still zero.
-    if (BN_abs_is_word(m, 1)) {
-      BN_zero(r);
-      return 1;
-    }
     return BN_one(r);
   }
 
@@ -586,6 +583,10 @@
 
 int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
                BN_CTX *ctx) {
+  if (m->neg) {
+    OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
+    return 0;
+  }
   if (a->neg || BN_ucmp(a, m) >= 0) {
     if (!BN_nnmod(r, a, m, ctx)) {
       return 0;
@@ -606,6 +607,10 @@
     OPENSSL_PUT_ERROR(BN, BN_R_CALLED_WITH_EVEN_MODULUS);
     return 0;
   }
+  if (m->neg) {
+    OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
+    return 0;
+  }
   if (a->neg || BN_ucmp(a, m) >= 0) {
     OPENSSL_PUT_ERROR(BN, BN_R_INPUT_NOT_REDUCED);
     return 0;
@@ -970,6 +975,10 @@
     OPENSSL_PUT_ERROR(BN, BN_R_CALLED_WITH_EVEN_MODULUS);
     return 0;
   }
+  if (m->neg) {
+    OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
+    return 0;
+  }
   if (a->neg || BN_ucmp(a, m) >= 0) {
     OPENSSL_PUT_ERROR(BN, BN_R_INPUT_NOT_REDUCED);
     return 0;
diff --git a/src/crypto/fipsmodule/bn/internal.h b/src/crypto/fipsmodule/bn/internal.h
index 9796831..fb8d11f 100644
--- a/src/crypto/fipsmodule/bn/internal.h
+++ b/src/crypto/fipsmodule/bn/internal.h
@@ -185,6 +185,10 @@
 #error "Must define either OPENSSL_32_BIT or OPENSSL_64_BIT"
 #endif
 
+#if !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__))
+#define BN_CAN_USE_INLINE_ASM
+#endif
+
 // |BN_mod_exp_mont_consttime| is based on the assumption that the L1 data
 // cache line width of the target processor is at least the following value.
 #define MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH 64
diff --git a/src/crypto/fipsmodule/bn/mul.c b/src/crypto/fipsmodule/bn/mul.c
index bd9393e..a1582a2 100644
--- a/src/crypto/fipsmodule/bn/mul.c
+++ b/src/crypto/fipsmodule/bn/mul.c
@@ -559,7 +559,7 @@
   BN_CTX_start(ctx);
   if (r == a || r == b) {
     rr = BN_CTX_get(ctx);
-    if (r == NULL) {
+    if (rr == NULL) {
       goto err;
     }
   } else {
diff --git a/src/crypto/fipsmodule/cipher/e_aes.c b/src/crypto/fipsmodule/cipher/e_aes.c
index de2f10f..0ced193 100644
--- a/src/crypto/fipsmodule/cipher/e_aes.c
+++ b/src/crypto/fipsmodule/cipher/e_aes.c
@@ -198,49 +198,45 @@
   if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE) && !enc) {
     if (hwaes_capable()) {
       ret = aes_hw_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)aes_hw_decrypt;
+      dat->block = aes_hw_decrypt;
       dat->stream.cbc = NULL;
       if (mode == EVP_CIPH_CBC_MODE) {
-        dat->stream.cbc = (cbc128_f)aes_hw_cbc_encrypt;
+        dat->stream.cbc = aes_hw_cbc_encrypt;
       }
     } else if (bsaes_capable() && mode == EVP_CIPH_CBC_MODE) {
       ret = AES_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)AES_decrypt;
-      dat->stream.cbc = (cbc128_f)bsaes_cbc_encrypt;
+      dat->block = AES_decrypt;
+      dat->stream.cbc = bsaes_cbc_encrypt;
     } else if (vpaes_capable()) {
       ret = vpaes_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)vpaes_decrypt;
-      dat->stream.cbc =
-          mode == EVP_CIPH_CBC_MODE ? (cbc128_f)vpaes_cbc_encrypt : NULL;
+      dat->block = vpaes_decrypt;
+      dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? vpaes_cbc_encrypt : NULL;
     } else {
       ret = AES_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)AES_decrypt;
-      dat->stream.cbc =
-          mode == EVP_CIPH_CBC_MODE ? (cbc128_f)AES_cbc_encrypt : NULL;
+      dat->block = AES_decrypt;
+      dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? AES_cbc_encrypt : NULL;
     }
   } else if (hwaes_capable()) {
     ret = aes_hw_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)aes_hw_encrypt;
+    dat->block = aes_hw_encrypt;
     dat->stream.cbc = NULL;
     if (mode == EVP_CIPH_CBC_MODE) {
-      dat->stream.cbc = (cbc128_f)aes_hw_cbc_encrypt;
+      dat->stream.cbc = aes_hw_cbc_encrypt;
     } else if (mode == EVP_CIPH_CTR_MODE) {
-      dat->stream.ctr = (ctr128_f)aes_hw_ctr32_encrypt_blocks;
+      dat->stream.ctr = aes_hw_ctr32_encrypt_blocks;
     }
   } else if (bsaes_capable() && mode == EVP_CIPH_CTR_MODE) {
     ret = AES_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)AES_encrypt;
-    dat->stream.ctr = (ctr128_f)bsaes_ctr32_encrypt_blocks;
+    dat->block = AES_encrypt;
+    dat->stream.ctr = bsaes_ctr32_encrypt_blocks;
   } else if (vpaes_capable()) {
     ret = vpaes_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)vpaes_encrypt;
-    dat->stream.cbc =
-        mode == EVP_CIPH_CBC_MODE ? (cbc128_f)vpaes_cbc_encrypt : NULL;
+    dat->block = vpaes_encrypt;
+    dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? vpaes_cbc_encrypt : NULL;
   } else {
     ret = AES_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)AES_encrypt;
-    dat->stream.cbc =
-        mode == EVP_CIPH_CBC_MODE ? (cbc128_f)AES_cbc_encrypt : NULL;
+    dat->block = AES_encrypt;
+    dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? AES_cbc_encrypt : NULL;
   }
 
   if (ret < 0) {
@@ -256,11 +252,11 @@
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
   if (dat->stream.cbc) {
-    (*dat->stream.cbc)(in, out, len, &dat->ks, ctx->iv, ctx->encrypt);
+    (*dat->stream.cbc)(in, out, len, &dat->ks.ks, ctx->iv, ctx->encrypt);
   } else if (ctx->encrypt) {
-    CRYPTO_cbc128_encrypt(in, out, len, &dat->ks, ctx->iv, dat->block);
+    CRYPTO_cbc128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, dat->block);
   } else {
-    CRYPTO_cbc128_decrypt(in, out, len, &dat->ks, ctx->iv, dat->block);
+    CRYPTO_cbc128_decrypt(in, out, len, &dat->ks.ks, ctx->iv, dat->block);
   }
 
   return 1;
@@ -277,7 +273,7 @@
 
   len -= bl;
   for (size_t i = 0; i <= len; i += bl) {
-    (*dat->block)(in + i, out + i, &dat->ks);
+    (*dat->block)(in + i, out + i, &dat->ks.ks);
   }
 
   return 1;
@@ -288,11 +284,11 @@
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
   if (dat->stream.ctr) {
-    CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks, ctx->iv, ctx->buf,
+    CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks.ks, ctx->iv, ctx->buf,
                                 &ctx->num, dat->stream.ctr);
   } else {
-    CRYPTO_ctr128_encrypt(in, out, len, &dat->ks, ctx->iv, ctx->buf, &ctx->num,
-                          dat->block);
+    CRYPTO_ctr128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, ctx->buf,
+                          &ctx->num, dat->block);
   }
   return 1;
 }
@@ -301,52 +297,53 @@
                           size_t len) {
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
-  CRYPTO_ofb128_encrypt(in, out, len, &dat->ks, ctx->iv, &ctx->num, dat->block);
+  CRYPTO_ofb128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, &ctx->num,
+                        dat->block);
   return 1;
 }
 
-ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx,
+ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_KEY *gcm_key,
                          block128_f *out_block, const uint8_t *key,
                          size_t key_bytes) {
   if (hwaes_capable()) {
     aes_hw_set_encrypt_key(key, key_bytes * 8, aes_key);
-    if (gcm_ctx != NULL) {
-      CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)aes_hw_encrypt, 1);
+    if (gcm_key != NULL) {
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, aes_hw_encrypt, 1);
     }
     if (out_block) {
-      *out_block = (block128_f) aes_hw_encrypt;
+      *out_block = aes_hw_encrypt;
     }
-    return (ctr128_f)aes_hw_ctr32_encrypt_blocks;
+    return aes_hw_ctr32_encrypt_blocks;
   }
 
   if (bsaes_capable()) {
     AES_set_encrypt_key(key, key_bytes * 8, aes_key);
-    if (gcm_ctx != NULL) {
-      CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)AES_encrypt, 0);
+    if (gcm_key != NULL) {
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, AES_encrypt, 0);
     }
     if (out_block) {
-      *out_block = (block128_f) AES_encrypt;
+      *out_block = AES_encrypt;
     }
-    return (ctr128_f)bsaes_ctr32_encrypt_blocks;
+    return bsaes_ctr32_encrypt_blocks;
   }
 
   if (vpaes_capable()) {
     vpaes_set_encrypt_key(key, key_bytes * 8, aes_key);
     if (out_block) {
-      *out_block = (block128_f) vpaes_encrypt;
+      *out_block = vpaes_encrypt;
     }
-    if (gcm_ctx != NULL) {
-      CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)vpaes_encrypt, 0);
+    if (gcm_key != NULL) {
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, vpaes_encrypt, 0);
     }
     return NULL;
   }
 
   AES_set_encrypt_key(key, key_bytes * 8, aes_key);
-  if (gcm_ctx != NULL) {
-    CRYPTO_gcm128_init(gcm_ctx, aes_key, (block128_f)AES_encrypt, 0);
+  if (gcm_key != NULL) {
+    CRYPTO_gcm128_init_key(gcm_key, aes_key, AES_encrypt, 0);
   }
   if (out_block) {
-    *out_block = (block128_f) AES_encrypt;
+    *out_block = AES_encrypt;
   }
   return NULL;
 }
@@ -358,8 +355,9 @@
     return 1;
   }
   if (key) {
-    gctx->ctr =
-        aes_ctr_set_key(&gctx->ks.ks, &gctx->gcm, NULL, key, ctx->key_len);
+    OPENSSL_memset(&gctx->gcm, 0, sizeof(gctx->gcm));
+    gctx->ctr = aes_ctr_set_key(&gctx->ks.ks, &gctx->gcm.gcm_key, NULL, key,
+                                ctx->key_len);
     // If we have an iv can set it directly, otherwise use saved IV.
     if (iv == NULL && gctx->iv_set) {
       iv = gctx->iv;
@@ -879,7 +877,7 @@
     double align;
     AES_KEY ks;
   } ks;
-  GCM128_CONTEXT gcm;
+  GCM128_KEY gcm_key;
   ctr128_f ctr;
 };
 
@@ -903,7 +901,7 @@
   }
 
   gcm_ctx->ctr =
-      aes_ctr_set_key(&gcm_ctx->ks.ks, &gcm_ctx->gcm, NULL, key, key_len);
+      aes_ctr_set_key(&gcm_ctx->ks.ks, &gcm_ctx->gcm_key, NULL, key, key_len);
   *out_tag_len = tag_len;
   return 1;
 }
@@ -941,7 +939,6 @@
                                      size_t extra_in_len,
                                      const uint8_t *ad, size_t ad_len) {
   const struct aead_aes_gcm_ctx *gcm_ctx = ctx->aead_state;
-  GCM128_CONTEXT gcm;
 
   if (extra_in_len + ctx->tag_len < ctx->tag_len) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_TOO_LARGE);
@@ -958,7 +955,9 @@
 
   const AES_KEY *key = &gcm_ctx->ks.ks;
 
-  OPENSSL_memcpy(&gcm, &gcm_ctx->gcm, sizeof(gcm));
+  GCM128_CONTEXT gcm;
+  OPENSSL_memset(&gcm, 0, sizeof(gcm));
+  OPENSSL_memcpy(&gcm.gcm_key, &gcm_ctx->gcm_key, sizeof(gcm.gcm_key));
   CRYPTO_gcm128_setiv(&gcm, key, nonce, nonce_len);
 
   if (ad_len > 0 && !CRYPTO_gcm128_aad(&gcm, ad, ad_len)) {
@@ -1002,7 +1001,6 @@
                                     const uint8_t *ad, size_t ad_len) {
   const struct aead_aes_gcm_ctx *gcm_ctx = ctx->aead_state;
   uint8_t tag[EVP_AEAD_AES_GCM_TAG_LEN];
-  GCM128_CONTEXT gcm;
 
   if (nonce_len == 0) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INVALID_NONCE_SIZE);
@@ -1016,7 +1014,9 @@
 
   const AES_KEY *key = &gcm_ctx->ks.ks;
 
-  OPENSSL_memcpy(&gcm, &gcm_ctx->gcm, sizeof(gcm));
+  GCM128_CONTEXT gcm;
+  OPENSSL_memset(&gcm, 0, sizeof(gcm));
+  OPENSSL_memcpy(&gcm.gcm_key, &gcm_ctx->gcm_key, sizeof(gcm.gcm_key));
   CRYPTO_gcm128_setiv(&gcm, key, nonce, nonce_len);
 
   if (!CRYPTO_gcm128_aad(&gcm, ad, ad_len)) {
diff --git a/src/crypto/fipsmodule/cipher/internal.h b/src/crypto/fipsmodule/cipher/internal.h
index 7b5f23f..7c739fb 100644
--- a/src/crypto/fipsmodule/cipher/internal.h
+++ b/src/crypto/fipsmodule/cipher/internal.h
@@ -114,11 +114,11 @@
 
 // aes_ctr_set_key initialises |*aes_key| using |key_bytes| bytes from |key|,
 // where |key_bytes| must either be 16, 24 or 32. If not NULL, |*out_block| is
-// set to a function that encrypts single blocks. If not NULL, |*gcm_ctx| is
+// set to a function that encrypts single blocks. If not NULL, |*gcm_key| is
 // initialised to do GHASH with the given key. It returns a function for
 // optimised CTR-mode, or NULL if CTR-mode should be built using
 // |*out_block|.
-ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_CONTEXT *gcm_ctx,
+ctr128_f aes_ctr_set_key(AES_KEY *aes_key, GCM128_KEY *gcm_key,
                          block128_f *out_block, const uint8_t *key,
                          size_t key_bytes);
 
diff --git a/src/crypto/fipsmodule/md5/md5.c b/src/crypto/fipsmodule/md5/md5.c
index 32429da..370b42a 100644
--- a/src/crypto/fipsmodule/md5/md5.c
+++ b/src/crypto/fipsmodule/md5/md5.c
@@ -85,11 +85,14 @@
     (defined(OPENSSL_X86_64) || defined(OPENSSL_X86))
 #define MD5_ASM
 #define md5_block_data_order md5_block_asm_data_order
+extern void md5_block_data_order(uint32_t *state, const uint8_t *data,
+                                 size_t num);
+#else
+static void md5_block_data_order(uint32_t *state, const uint8_t *data,
+                                 size_t num);
 #endif
 
 
-void md5_block_data_order(uint32_t *state, const uint8_t *data, size_t num);
-
 #define DATA_ORDER_IS_LITTLE_ENDIAN
 
 #define HASH_CTX MD5_CTX
@@ -151,11 +154,12 @@
     (a) += (b);                            \
   } while (0)
 
-#ifndef md5_block_data_order
+#ifndef MD5_ASM
 #ifdef X
 #undef X
 #endif
-void md5_block_data_order(uint32_t *state, const uint8_t *data, size_t num) {
+static void md5_block_data_order(uint32_t *state, const uint8_t *data,
+                                 size_t num) {
   uint32_t A, B, C, D, l;
   uint32_t XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12,
       XX13, XX14, XX15;
diff --git a/src/crypto/fipsmodule/modes/cbc.c b/src/crypto/fipsmodule/modes/cbc.c
index db9f024..64ea505 100644
--- a/src/crypto/fipsmodule/modes/cbc.c
+++ b/src/crypto/fipsmodule/modes/cbc.c
@@ -53,7 +53,7 @@
 
 
 void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            block128_f block) {
   size_t n;
   const uint8_t *iv = ivec;
@@ -108,7 +108,7 @@
 }
 
 void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            block128_f block) {
   size_t n;
   union {
diff --git a/src/crypto/fipsmodule/modes/ccm.c b/src/crypto/fipsmodule/modes/ccm.c
index 784e4fa..5a153f4 100644
--- a/src/crypto/fipsmodule/modes/ccm.c
+++ b/src/crypto/fipsmodule/modes/ccm.c
@@ -64,8 +64,8 @@
   } nonce, cmac;
 };
 
-int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const void *key, block128_f block,
-                       ctr128_f ctr, unsigned M, unsigned L) {
+int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const AES_KEY *key,
+                       block128_f block, ctr128_f ctr, unsigned M, unsigned L) {
   if (M < 4 || M > 16 || (M & 1) != 0 || L < 2 || L > 8) {
     return 0;
   }
@@ -82,7 +82,7 @@
 }
 
 static int ccm128_init_state(const CCM128_CONTEXT *ctx,
-                             struct ccm128_state *state, const void *key,
+                             struct ccm128_state *state, const AES_KEY *key,
                              const uint8_t *nonce, size_t nonce_len,
                              const uint8_t *aad, size_t aad_len,
                              size_t plaintext_len) {
@@ -170,7 +170,7 @@
 }
 
 static int ccm128_encrypt(const CCM128_CONTEXT *ctx, struct ccm128_state *state,
-                          const void *key, uint8_t *out, const uint8_t *in,
+                          const AES_KEY *key, uint8_t *out, const uint8_t *in,
                           size_t len) {
   // The counter for encryption begins at one.
   for (unsigned i = 0; i < ctx->L; i++) {
@@ -191,7 +191,7 @@
 }
 
 static int ccm128_compute_mac(const CCM128_CONTEXT *ctx,
-                              struct ccm128_state *state, const void *key,
+                              struct ccm128_state *state, const AES_KEY *key,
                               uint8_t *out_tag, size_t tag_len,
                               const uint8_t *in, size_t len) {
   block128_f block = ctx->block;
@@ -231,7 +231,7 @@
   return 1;
 }
 
-int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -243,7 +243,7 @@
          ccm128_encrypt(ctx, &state, key, out, in, len);
 }
 
-int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
diff --git a/src/crypto/fipsmodule/modes/cfb.c b/src/crypto/fipsmodule/modes/cfb.c
index e1b0a80..d3a38d6 100644
--- a/src/crypto/fipsmodule/modes/cfb.c
+++ b/src/crypto/fipsmodule/modes/cfb.c
@@ -57,7 +57,7 @@
 OPENSSL_COMPILE_ASSERT((16 % sizeof(size_t)) == 0, bad_size_t_size_cfb);
 
 void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            int enc, block128_f block) {
   size_t l = 0;
 
@@ -161,7 +161,7 @@
 /* This expects a single block of size nbits for both in and out. Note that
    it corrupts any extra bits in the last byte of out */
 static void cfbr_encrypt_block(const uint8_t *in, uint8_t *out, unsigned nbits,
-                               const void *key, uint8_t ivec[16], int enc,
+                               const AES_KEY *key, uint8_t ivec[16], int enc,
                                block128_f block) {
   int n, rem, num;
   uint8_t ovec[16 * 2 + 1]; /* +1 because we dererefence (but don't use) one
@@ -203,8 +203,8 @@
 
 // N.B. This expects the input to be packed, MS bit first
 void CRYPTO_cfb128_1_encrypt(const uint8_t *in, uint8_t *out, size_t bits,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block) {
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block) {
   size_t n;
   uint8_t c[1], d[1];
 
@@ -220,7 +220,7 @@
 }
 
 void CRYPTO_cfb128_8_encrypt(const unsigned char *in, unsigned char *out,
-                             size_t length, const void *key,
+                             size_t length, const AES_KEY *key,
                              unsigned char ivec[16], unsigned *num, int enc,
                              block128_f block) {
   size_t n;
diff --git a/src/crypto/fipsmodule/modes/ctr.c b/src/crypto/fipsmodule/modes/ctr.c
index 63907b4..5093408 100644
--- a/src/crypto/fipsmodule/modes/ctr.c
+++ b/src/crypto/fipsmodule/modes/ctr.c
@@ -82,7 +82,7 @@
 // of the IV.  This implementation takes NO responsibility for checking that
 // the counter doesn't overflow into the rest of the IV when incremented.
 void CRYPTO_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            uint8_t ecount_buf[16], unsigned int *num,
                            block128_f block) {
   unsigned int n;
@@ -153,11 +153,10 @@
   } while (n);
 }
 
-void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out,
-                                 size_t len, const void *key,
-                                 uint8_t ivec[16],
-                                 uint8_t ecount_buf[16],
-                                 unsigned int *num, ctr128_f func) {
+void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len,
+                                 const AES_KEY *key, uint8_t ivec[16],
+                                 uint8_t ecount_buf[16], unsigned int *num,
+                                 ctr128_f func) {
   unsigned int n, ctr32;
 
   assert(key && ecount_buf && num);
diff --git a/src/crypto/fipsmodule/modes/gcm.c b/src/crypto/fipsmodule/modes/gcm.c
index 99d0e15..5e556df 100644
--- a/src/crypto/fipsmodule/modes/gcm.c
+++ b/src/crypto/fipsmodule/modes/gcm.c
@@ -243,9 +243,10 @@
                     size_t len);
 #endif
 
-#define GCM_MUL(ctx, Xi) gcm_gmult_4bit((ctx)->Xi.u, (ctx)->Htable)
+#define GCM_MUL(ctx, Xi) gcm_gmult_4bit((ctx)->Xi.u, (ctx)->gcm_key.Htable)
 #if defined(GHASH_ASM)
-#define GHASH(ctx, in, len) gcm_ghash_4bit((ctx)->Xi.u, (ctx)->Htable, in, len)
+#define GHASH(ctx, in, len) \
+  gcm_ghash_4bit((ctx)->Xi.u, (ctx)->gcm_key.Htable, in, len)
 // GHASH_CHUNK is "stride parameter" missioned to mitigate cache
 // trashing effect. In other words idea is to hash data while it's
 // still in L1 cache after encryption pass...
@@ -270,9 +271,9 @@
                    size_t len);
 #define AESNI_GCM
 size_t aesni_gcm_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], uint64_t *Xi);
+                         const AES_KEY *key, uint8_t ivec[16], uint64_t *Xi);
 size_t aesni_gcm_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], uint64_t *Xi);
+                         const AES_KEY *key, uint8_t ivec[16], uint64_t *Xi);
 #endif
 
 #if defined(OPENSSL_X86)
@@ -337,10 +338,11 @@
 
 #ifdef GCM_FUNCREF_4BIT
 #undef GCM_MUL
-#define GCM_MUL(ctx, Xi) (*gcm_gmult_p)((ctx)->Xi.u, (ctx)->Htable)
+#define GCM_MUL(ctx, Xi) (*gcm_gmult_p)((ctx)->Xi.u, (ctx)->gcm_key.Htable)
 #ifdef GHASH
 #undef GHASH
-#define GHASH(ctx, in, len) (*gcm_ghash_p)((ctx)->Xi.u, (ctx)->Htable, in, len)
+#define GHASH(ctx, in, len) \
+  (*gcm_ghash_p)((ctx)->Xi.u, (ctx)->gcm_key.Htable, in, len)
 #endif
 #endif
 
@@ -417,27 +419,28 @@
 #endif
 }
 
-void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, const void *aes_key,
-                        block128_f block, int block_is_hwaes) {
-  OPENSSL_memset(ctx, 0, sizeof(*ctx));
-  ctx->block = block;
+void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key, const AES_KEY *aes_key,
+                            block128_f block, int block_is_hwaes) {
+  OPENSSL_memset(gcm_key, 0, sizeof(*gcm_key));
+  gcm_key->block = block;
 
-  uint8_t gcm_key[16];
-  OPENSSL_memset(gcm_key, 0, sizeof(gcm_key));
-  (*block)(gcm_key, gcm_key, aes_key);
+  uint8_t ghash_key[16];
+  OPENSSL_memset(ghash_key, 0, sizeof(ghash_key));
+  (*block)(ghash_key, ghash_key, aes_key);
 
   int is_avx;
-  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, &ctx->H, ctx->Htable, &is_avx,
-                    gcm_key);
+  CRYPTO_ghash_init(&gcm_key->gmult, &gcm_key->ghash, &gcm_key->H,
+                    gcm_key->Htable, &is_avx, ghash_key);
 
-  ctx->use_aesni_gcm_crypt = (is_avx && block_is_hwaes) ? 1 : 0;
+  gcm_key->use_aesni_gcm_crypt = (is_avx && block_is_hwaes) ? 1 : 0;
 }
 
-void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const void *key,
+void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
                          const uint8_t *iv, size_t len) {
   unsigned int ctr;
 #ifdef GCM_FUNCREF_4BIT
-  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) =
+      ctx->gcm_key.gmult;
 #endif
 
   ctx->Yi.u[0] = 0;
@@ -477,7 +480,7 @@
     ctr = CRYPTO_bswap4(ctx->Yi.d[3]);
   }
 
-  (*ctx->block)(ctx->Yi.c, ctx->EK0.c, key);
+  (*ctx->gcm_key.block)(ctx->Yi.c, ctx->EK0.c, key);
   ++ctr;
   ctx->Yi.d[3] = CRYPTO_bswap4(ctr);
 }
@@ -486,10 +489,11 @@
   unsigned int n;
   uint64_t alen = ctx->len.u[0];
 #ifdef GCM_FUNCREF_4BIT
-  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) =
+      ctx->gcm_key.gmult;
 #ifdef GHASH
   void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
-                      size_t len) = ctx->ghash;
+                      size_t len) = ctx->gcm_key.ghash;
 #endif
 #endif
 
@@ -549,16 +553,17 @@
   return 1;
 }
 
-int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
                           const uint8_t *in, uint8_t *out, size_t len) {
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
-  block128_f block = ctx->block;
+  block128_f block = ctx->gcm_key.block;
 #ifdef GCM_FUNCREF_4BIT
-  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) =
+      ctx->gcm_key.gmult;
 #ifdef GHASH
   void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
-                      size_t len) = ctx->ghash;
+                      size_t len) = ctx->gcm_key.ghash;
 #endif
 #endif
 
@@ -674,17 +679,18 @@
   return 1;
 }
 
-int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
                           const unsigned char *in, unsigned char *out,
                           size_t len) {
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
-  block128_f block = ctx->block;
+  block128_f block = ctx->gcm_key.block;
 #ifdef GCM_FUNCREF_4BIT
-  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) =
+      ctx->gcm_key.gmult;
 #ifdef GHASH
   void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
-                      size_t len) = ctx->ghash;
+                      size_t len) = ctx->gcm_key.ghash;
 #endif
 #endif
 
@@ -807,16 +813,17 @@
   return 1;
 }
 
-int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                 const uint8_t *in, uint8_t *out, size_t len,
                                 ctr128_f stream) {
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
 #ifdef GCM_FUNCREF_4BIT
-  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) =
+      ctx->gcm_key.gmult;
 #ifdef GHASH
   void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
-                      size_t len) = ctx->ghash;
+                      size_t len) = ctx->gcm_key.ghash;
 #endif
 #endif
 
@@ -849,7 +856,7 @@
   }
 
 #if defined(AESNI_GCM)
-  if (ctx->use_aesni_gcm_crypt) {
+  if (ctx->gcm_key.use_aesni_gcm_crypt) {
     // |aesni_gcm_encrypt| may not process all the input given to it. It may
     // not process *any* of its input if it is deemed too small.
     size_t bulk = aesni_gcm_encrypt(in, out, len, key, ctx->Yi.c, ctx->Xi.u);
@@ -895,7 +902,7 @@
 #endif
   }
   if (len) {
-    (*ctx->block)(ctx->Yi.c, ctx->EKi.c, key);
+    (*ctx->gcm_key.block)(ctx->Yi.c, ctx->EKi.c, key);
     ++ctr;
     ctx->Yi.d[3] = CRYPTO_bswap4(ctr);
     while (len--) {
@@ -908,16 +915,17 @@
   return 1;
 }
 
-int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                 const uint8_t *in, uint8_t *out, size_t len,
                                 ctr128_f stream) {
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
 #ifdef GCM_FUNCREF_4BIT
-  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) =
+      ctx->gcm_key.gmult;
 #ifdef GHASH
   void (*gcm_ghash_p)(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
-                      size_t len) = ctx->ghash;
+                      size_t len) = ctx->gcm_key.ghash;
 #endif
 #endif
 
@@ -952,7 +960,7 @@
   }
 
 #if defined(AESNI_GCM)
-  if (ctx->use_aesni_gcm_crypt) {
+  if (ctx->gcm_key.use_aesni_gcm_crypt) {
     // |aesni_gcm_decrypt| may not process all the input given to it. It may
     // not process *any* of its input if it is deemed too small.
     size_t bulk = aesni_gcm_decrypt(in, out, len, key, ctx->Yi.c, ctx->Xi.u);
@@ -1001,7 +1009,7 @@
     len -= i;
   }
   if (len) {
-    (*ctx->block)(ctx->Yi.c, ctx->EKi.c, key);
+    (*ctx->gcm_key.block)(ctx->Yi.c, ctx->EKi.c, key);
     ++ctr;
     ctx->Yi.d[3] = CRYPTO_bswap4(ctr);
     while (len--) {
@@ -1020,7 +1028,8 @@
   uint64_t alen = ctx->len.u[0] << 3;
   uint64_t clen = ctx->len.u[1] << 3;
 #ifdef GCM_FUNCREF_4BIT
-  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) = ctx->gmult;
+  void (*gcm_gmult_p)(uint64_t Xi[2], const u128 Htable[16]) =
+      ctx->gcm_key.gmult;
 #endif
 
   if (ctx->mres || ctx->ares) {
diff --git a/src/crypto/fipsmodule/modes/gcm_test.cc b/src/crypto/fipsmodule/modes/gcm_test.cc
index 5988945..30308f8 100644
--- a/src/crypto/fipsmodule/modes/gcm_test.cc
+++ b/src/crypto/fipsmodule/modes/gcm_test.cc
@@ -87,7 +87,8 @@
     ASSERT_EQ(0, AES_set_encrypt_key(key.data(), key.size() * 8, &aes_key));
 
     GCM128_CONTEXT ctx;
-    CRYPTO_gcm128_init(&ctx, &aes_key, (block128_f)AES_encrypt, 0);
+    OPENSSL_memset(&ctx, 0, sizeof(ctx));
+    CRYPTO_gcm128_init_key(&ctx.gcm_key, &aes_key, AES_encrypt, 0);
     CRYPTO_gcm128_setiv(&ctx, &aes_key, nonce.data(), nonce.size());
     if (!additional_data.empty()) {
       CRYPTO_gcm128_aad(&ctx, additional_data.data(), additional_data.size());
diff --git a/src/crypto/fipsmodule/modes/internal.h b/src/crypto/fipsmodule/modes/internal.h
index 338bf13..23aaca2 100644
--- a/src/crypto/fipsmodule/modes/internal.h
+++ b/src/crypto/fipsmodule/modes/internal.h
@@ -50,6 +50,7 @@
 #define OPENSSL_HEADER_MODES_INTERNAL_H
 
 #include <openssl/base.h>
+#include <openssl/aes.h>
 
 #include <string.h>
 
@@ -87,11 +88,60 @@
   OPENSSL_memcpy(out, &v, sizeof(v));
 }
 
-// block128_f is the type of a 128-bit, block cipher.
+// block128_f is the type of an AES block cipher implementation.
+//
+// Unlike upstream OpenSSL, it and the other functions in this file hard-code
+// |AES_KEY|. It is undefined in C to call a function pointer with anything
+// other than the original type. Thus we either must match |block128_f| to the
+// type signature of |AES_encrypt| and friends or pass in |void*| wrapper
+// functions.
+//
+// These functions are called exclusively with AES, so we use the former.
 typedef void (*block128_f)(const uint8_t in[16], uint8_t out[16],
-                           const void *key);
+                           const AES_KEY *key);
 
-// GCM definitions
+
+// CTR.
+
+// ctr128_f is the type of a function that performs CTR-mode encryption.
+typedef void (*ctr128_f)(const uint8_t *in, uint8_t *out, size_t blocks,
+                         const AES_KEY *key, const uint8_t ivec[16]);
+
+// CRYPTO_ctr128_encrypt encrypts (or decrypts, it's the same in CTR mode)
+// |len| bytes from |in| to |out| using |block| in counter mode. There's no
+// requirement that |len| be a multiple of any value and any partial blocks are
+// stored in |ecount_buf| and |*num|, which must be zeroed before the initial
+// call. The counter is a 128-bit, big-endian value in |ivec| and is
+// incremented by this function.
+void CRYPTO_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const AES_KEY *key, uint8_t ivec[16],
+                           uint8_t ecount_buf[16], unsigned *num,
+                           block128_f block);
+
+// CRYPTO_ctr128_encrypt_ctr32 acts like |CRYPTO_ctr128_encrypt| but takes
+// |ctr|, a function that performs CTR mode but only deals with the lower 32
+// bits of the counter. This is useful when |ctr| can be an optimised
+// function.
+void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len,
+                                 const AES_KEY *key, uint8_t ivec[16],
+                                 uint8_t ecount_buf[16], unsigned *num,
+                                 ctr128_f ctr);
+
+#if !defined(OPENSSL_NO_ASM) && \
+    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
+void aesni_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t blocks,
+                                const AES_KEY *key, const uint8_t *ivec);
+#endif
+
+
+// GCM.
+//
+// This API differs from the upstream API slightly. The |GCM128_CONTEXT| does
+// not have a |key| pointer that points to the key as upstream's version does.
+// Instead, every function takes a |key| parameter. This way |GCM128_CONTEXT|
+// can be safely copied. Additionally, |gcm_key| is split into a separate
+// struct.
+
 typedef struct { uint64_t hi,lo; } u128;
 
 // gmult_func multiplies |Xi| by the GCM key and writes the result back to
@@ -104,11 +154,25 @@
 typedef void (*ghash_func)(uint64_t Xi[2], const u128 Htable[16],
                            const uint8_t *inp, size_t len);
 
-// This differs from upstream's |gcm128_context| in that it does not have the
-// |key| pointer, in order to make it |memcpy|-friendly. Rather the key is
-// passed into each call that needs it.
-struct gcm128_context {
-  // Following 6 names follow names in GCM specification
+typedef struct gcm128_key_st {
+  // Note the MOVBE-based, x86-64, GHASH assembly requires |H| and |Htable| to
+  // be the first two elements of this struct.
+  u128 H;
+  u128 Htable[16];
+  gmult_func gmult;
+  ghash_func ghash;
+
+  block128_f block;
+
+  // use_aesni_gcm_crypt is true if this context should use the assembly
+  // functions |aesni_gcm_encrypt| and |aesni_gcm_decrypt| to process data.
+  unsigned use_aesni_gcm_crypt:1;
+} GCM128_KEY;
+
+// GCM128_CONTEXT contains state for a single GCM operation. The structure
+// should be zero-initialized before use.
+typedef struct {
+  // The following 5 names follow names in GCM specification
   union {
     uint64_t u[2];
     uint32_t d[4];
@@ -116,20 +180,12 @@
     size_t t[16 / sizeof(size_t)];
   } Yi, EKi, EK0, len, Xi;
 
-  // Note that the order of |Xi|, |H| and |Htable| is fixed by the MOVBE-based,
+  // Note that the order of |Xi| and |gcm_key| is fixed by the MOVBE-based,
   // x86-64, GHASH assembly.
-  u128 H;
-  u128 Htable[16];
-  gmult_func gmult;
-  ghash_func ghash;
+  GCM128_KEY gcm_key;
 
-  unsigned int mres, ares;
-  block128_f block;
-
-  // use_aesni_gcm_crypt is true if this context should use the assembly
-  // functions |aesni_gcm_encrypt| and |aesni_gcm_decrypt| to process data.
-  unsigned use_aesni_gcm_crypt:1;
-};
+  unsigned mres, ares;
+} GCM128_CONTEXT;
 
 #if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
 // crypto_gcm_clmul_enabled returns one if the CLMUL implementation of GCM is
@@ -137,49 +193,6 @@
 int crypto_gcm_clmul_enabled(void);
 #endif
 
-
-// CTR.
-
-// ctr128_f is the type of a function that performs CTR-mode encryption.
-typedef void (*ctr128_f)(const uint8_t *in, uint8_t *out, size_t blocks,
-                         const void *key, const uint8_t ivec[16]);
-
-// CRYPTO_ctr128_encrypt encrypts (or decrypts, it's the same in CTR mode)
-// |len| bytes from |in| to |out| using |block| in counter mode. There's no
-// requirement that |len| be a multiple of any value and any partial blocks are
-// stored in |ecount_buf| and |*num|, which must be zeroed before the initial
-// call. The counter is a 128-bit, big-endian value in |ivec| and is
-// incremented by this function.
-void CRYPTO_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
-                           uint8_t ecount_buf[16], unsigned *num,
-                           block128_f block);
-
-// CRYPTO_ctr128_encrypt_ctr32 acts like |CRYPTO_ctr128_encrypt| but takes
-// |ctr|, a function that performs CTR mode but only deals with the lower 32
-// bits of the counter. This is useful when |ctr| can be an optimised
-// function.
-void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len,
-                                 const void *key, uint8_t ivec[16],
-                                 uint8_t ecount_buf[16], unsigned *num,
-                                 ctr128_f ctr);
-
-#if !defined(OPENSSL_NO_ASM) && \
-    (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
-void aesni_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t blocks,
-                                const void *key, const uint8_t *ivec);
-#endif
-
-
-// GCM.
-//
-// This API differs from the upstream API slightly. The |GCM128_CONTEXT| does
-// not have a |key| pointer that points to the key as upstream's version does.
-// Instead, every function takes a |key| parameter. This way |GCM128_CONTEXT|
-// can be safely copied.
-
-typedef struct gcm128_context GCM128_CONTEXT;
-
 // CRYPTO_ghash_init writes a precomputed table of powers of |gcm_key| to
 // |out_table| and sets |*out_mult| and |*out_hash| to (potentially hardware
 // accelerated) functions for performing operations in the GHASH field. If the
@@ -188,14 +201,15 @@
                        u128 *out_key, u128 out_table[16], int *out_is_avx,
                        const uint8_t *gcm_key);
 
-// CRYPTO_gcm128_init initialises |ctx| to use |block| (typically AES) with
-// the given key. |block_is_hwaes| is one if |block| is |aes_hw_encrypt|.
-OPENSSL_EXPORT void CRYPTO_gcm128_init(GCM128_CONTEXT *ctx, const void *key,
-                                       block128_f block, int block_is_hwaes);
+// CRYPTO_gcm128_init_key initialises |gcm_key| to use |block| (typically AES)
+// with the given key. |block_is_hwaes| is one if |block| is |aes_hw_encrypt|.
+OPENSSL_EXPORT void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key,
+                                           const AES_KEY *key, block128_f block,
+                                           int block_is_hwaes);
 
 // CRYPTO_gcm128_setiv sets the IV (nonce) for |ctx|. The |key| must be the
 // same key that was passed to |CRYPTO_gcm128_init|.
-OPENSSL_EXPORT void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const void *key,
+OPENSSL_EXPORT void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                         const uint8_t *iv, size_t iv_len);
 
 // CRYPTO_gcm128_aad sets the authenticated data for an instance of GCM.
@@ -207,16 +221,16 @@
 // CRYPTO_gcm128_encrypt encrypts |len| bytes from |in| to |out|. The |key|
 // must be the same key that was passed to |CRYPTO_gcm128_init|. It returns one
 // on success and zero otherwise.
-OPENSSL_EXPORT int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const void *key,
-                                         const uint8_t *in, uint8_t *out,
-                                         size_t len);
+OPENSSL_EXPORT int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
+                                         const AES_KEY *key, const uint8_t *in,
+                                         uint8_t *out, size_t len);
 
 // CRYPTO_gcm128_decrypt decrypts |len| bytes from |in| to |out|. The |key|
 // must be the same key that was passed to |CRYPTO_gcm128_init|. It returns one
 // on success and zero otherwise.
-OPENSSL_EXPORT int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const void *key,
-                                         const uint8_t *in, uint8_t *out,
-                                         size_t len);
+OPENSSL_EXPORT int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
+                                         const AES_KEY *key, const uint8_t *in,
+                                         uint8_t *out, size_t len);
 
 // CRYPTO_gcm128_encrypt_ctr32 encrypts |len| bytes from |in| to |out| using
 // a CTR function that only handles the bottom 32 bits of the nonce, like
@@ -224,7 +238,7 @@
 // passed to |CRYPTO_gcm128_init|. It returns one on success and zero
 // otherwise.
 OPENSSL_EXPORT int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
-                                               const void *key,
+                                               const AES_KEY *key,
                                                const uint8_t *in, uint8_t *out,
                                                size_t len, ctr128_f stream);
 
@@ -234,7 +248,7 @@
 // passed to |CRYPTO_gcm128_init|. It returns one on success and zero
 // otherwise.
 OPENSSL_EXPORT int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
-                                               const void *key,
+                                               const AES_KEY *key,
                                                const uint8_t *in, uint8_t *out,
                                                size_t len, ctr128_f stream);
 
@@ -260,8 +274,8 @@
 // CRYPTO_ccm128_init initialises |ctx| to use |block| (typically AES) with the
 // specified |M| and |L| parameters. It returns one on success and zero if |M|
 // or |L| is invalid.
-int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const void *key, block128_f block,
-                       ctr128_f ctr, unsigned M, unsigned L);
+int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const AES_KEY *key,
+                       block128_f block, ctr128_f ctr, unsigned M, unsigned L);
 
 // CRYPTO_ccm128_max_input returns the maximum input length accepted by |ctx|.
 size_t CRYPTO_ccm128_max_input(const CCM128_CONTEXT *ctx);
@@ -269,7 +283,7 @@
 // CRYPTO_ccm128_encrypt encrypts |len| bytes from |in| to |out| writing the tag
 // to |out_tag|. |key| must be the same key that was passed to
 // |CRYPTO_ccm128_init|. It returns one on success and zero otherwise.
-int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -278,7 +292,7 @@
 // CRYPTO_ccm128_decrypt decrypts |len| bytes from |in| to |out|, writing the
 // expected tag to |out_tag|. |key| must be the same key that was passed to
 // |CRYPTO_ccm128_init|. It returns one on success and zero otherwise.
-int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -289,21 +303,23 @@
 
 // cbc128_f is the type of a function that performs CBC-mode encryption.
 typedef void (*cbc128_f)(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], int enc);
+                         const AES_KEY *key, uint8_t ivec[16], int enc);
 
 // CRYPTO_cbc128_encrypt encrypts |len| bytes from |in| to |out| using the
 // given IV and block cipher in CBC mode. The input need not be a multiple of
 // 128 bits long, but the output will round up to the nearest 128 bit multiple,
 // zero padding the input if needed. The IV will be updated on return.
 void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], block128_f block);
+                           const AES_KEY *key, uint8_t ivec[16],
+                           block128_f block);
 
 // CRYPTO_cbc128_decrypt decrypts |len| bytes from |in| to |out| using the
 // given IV and block cipher in CBC mode. If |len| is not a multiple of 128
 // bits then only that many bytes will be written, but a multiple of 128 bits
 // is always read from |in|. The IV will be updated on return.
 void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], block128_f block);
+                           const AES_KEY *key, uint8_t ivec[16],
+                           block128_f block);
 
 
 // OFB.
@@ -313,9 +329,9 @@
 // requirement that |len| be a multiple of any value and any partial blocks are
 // stored in |ivec| and |*num|, the latter must be zero before the initial
 // call.
-void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out,
-                           size_t len, const void *key, uint8_t ivec[16],
-                           unsigned *num, block128_f block);
+void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
+                           block128_f block);
 
 
 // CFB.
@@ -325,25 +341,25 @@
 // |len| be a multiple of any value and any partial blocks are stored in |ivec|
 // and |*num|, the latter must be zero before the initial call.
 void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            int enc, block128_f block);
 
 // CRYPTO_cfb128_8_encrypt encrypts (or decrypts, if |enc| is zero) |len| bytes
 // from |in| to |out| using |block| in CFB-8 mode. Prior to the first call
 // |num| should be set to zero.
 void CRYPTO_cfb128_8_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block);
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block);
 
 // CRYPTO_cfb128_1_encrypt encrypts (or decrypts, if |enc| is zero) |len| bytes
 // from |in| to |out| using |block| in CFB-1 mode. Prior to the first call
 // |num| should be set to zero.
 void CRYPTO_cfb128_1_encrypt(const uint8_t *in, uint8_t *out, size_t bits,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block);
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block);
 
 size_t CRYPTO_cts128_encrypt_block(const uint8_t *in, uint8_t *out, size_t len,
-                                   const void *key, uint8_t ivec[16],
+                                   const AES_KEY *key, uint8_t ivec[16],
                                    block128_f block);
 
 
diff --git a/src/crypto/fipsmodule/modes/ofb.c b/src/crypto/fipsmodule/modes/ofb.c
index 63bba68..b1b4d87 100644
--- a/src/crypto/fipsmodule/modes/ofb.c
+++ b/src/crypto/fipsmodule/modes/ofb.c
@@ -57,7 +57,7 @@
 OPENSSL_COMPILE_ASSERT((16 % sizeof(size_t)) == 0, bad_size_t_size_ofb);
 
 void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            block128_f block) {
   assert(in && out && key && ivec && num);
 
diff --git a/src/crypto/fipsmodule/rand/urandom.c b/src/crypto/fipsmodule/rand/urandom.c
index e6d779c..4923fa4 100644
--- a/src/crypto/fipsmodule/rand/urandom.c
+++ b/src/crypto/fipsmodule/rand/urandom.c
@@ -73,6 +73,27 @@
 
 #endif  // __NR_getrandom
 
+#if defined(OPENSSL_MSAN)
+void __msan_unpoison(void *, size_t);
+#endif
+
+static ssize_t boringssl_getrandom(void *buf, size_t buf_len, unsigned flags) {
+  ssize_t ret;
+  do {
+    ret = syscall(__NR_getrandom, buf, buf_len, flags);
+  } while (ret == -1 && errno == EINTR);
+
+#if defined(OPENSSL_MSAN)
+  if (ret > 0) {
+    // MSAN doesn't recognise |syscall| and thus doesn't notice that we have
+    // initialised the output buffer.
+    __msan_unpoison(buf, ret);
+  }
+#endif  // OPENSSL_MSAN
+
+  return ret;
+}
+
 #endif  // EXPECTED_NR_getrandom
 
 #if !defined(GRND_NONBLOCK)
@@ -108,28 +129,30 @@
 
 #if defined(USE_NR_getrandom)
   uint8_t dummy;
-  long getrandom_ret =
-      syscall(__NR_getrandom, &dummy, sizeof(dummy), GRND_NONBLOCK);
+  ssize_t getrandom_ret =
+      boringssl_getrandom(&dummy, sizeof(dummy), GRND_NONBLOCK);
 
-  if (getrandom_ret == 1) {
-    *urandom_fd_bss_get() = kHaveGetrandom;
-    return;
-  } else if (getrandom_ret == -1 && errno == EAGAIN) {
+  if (getrandom_ret == -1 && errno == EAGAIN) {
     fprintf(
         stderr,
         "getrandom indicates that the entropy pool has not been initialized. "
         "Rather than continue with poor entropy, this process will block until "
         "entropy is available.\n");
 
-    do {
-      getrandom_ret =
-          syscall(__NR_getrandom, &dummy, sizeof(dummy), 0 /* no flags */);
-    } while (getrandom_ret == -1 && errno == EINTR);
+    getrandom_ret =
+        boringssl_getrandom(&dummy, sizeof(dummy), 0 /* no flags */);
+  }
 
-    if (getrandom_ret == 1) {
-      *urandom_fd_bss_get() = kHaveGetrandom;
-      return;
-    }
+  if (getrandom_ret == 1) {
+    *urandom_fd_bss_get() = kHaveGetrandom;
+    return;
+  }
+
+  // Ignore ENOSYS and fallthrough to using /dev/urandom, below. Otherwise it's
+  // a fatal error.
+  if (getrandom_ret != -1 || errno != ENOSYS) {
+    perror("getrandom");
+    abort();
   }
 #endif  // USE_NR_getrandom
 
@@ -232,10 +255,6 @@
   }
 }
 
-#if defined(USE_NR_getrandom) && defined(OPENSSL_MSAN)
-void __msan_unpoison(void *, size_t);
-#endif
-
 // fill_with_entropy writes |len| bytes of entropy into |out|. It returns one
 // on success and zero on error.
 static char fill_with_entropy(uint8_t *out, size_t len) {
@@ -244,18 +263,7 @@
 
     if (*urandom_fd_bss_get() == kHaveGetrandom) {
 #if defined(USE_NR_getrandom)
-      do {
-        r = syscall(__NR_getrandom, out, len, 0 /* no flags */);
-      } while (r == -1 && errno == EINTR);
-
-#if defined(OPENSSL_MSAN)
-      if (r > 0) {
-        // MSAN doesn't recognise |syscall| and thus doesn't notice that we
-        // have initialised the output buffer.
-        __msan_unpoison(out, r);
-      }
-#endif  // OPENSSL_MSAN
-
+      r = boringssl_getrandom(out, len, 0 /* no flags */);
 #else  // USE_NR_getrandom
       fprintf(stderr, "urandom fd corrupt.\n");
       abort();
diff --git a/src/crypto/fipsmodule/rsa/padding.c b/src/crypto/fipsmodule/rsa/padding.c
index ce3df7a..b7998fe 100644
--- a/src/crypto/fipsmodule/rsa/padding.c
+++ b/src/crypto/fipsmodule/rsa/padding.c
@@ -480,7 +480,7 @@
 
 static const uint8_t kPSSZeroes[] = {0, 0, 0, 0, 0, 0, 0, 0};
 
-int RSA_verify_PKCS1_PSS_mgf1(RSA *rsa, const uint8_t *mHash,
+int RSA_verify_PKCS1_PSS_mgf1(const RSA *rsa, const uint8_t *mHash,
                               const EVP_MD *Hash, const EVP_MD *mgf1Hash,
                               const uint8_t *EM, int sLen) {
   int i;
@@ -579,7 +579,7 @@
   return ret;
 }
 
-int RSA_padding_add_PKCS1_PSS_mgf1(RSA *rsa, unsigned char *EM,
+int RSA_padding_add_PKCS1_PSS_mgf1(const RSA *rsa, unsigned char *EM,
                                    const unsigned char *mHash,
                                    const EVP_MD *Hash, const EVP_MD *mgf1Hash,
                                    int sLenRequested) {
diff --git a/src/crypto/fipsmodule/self_check/self_check.c b/src/crypto/fipsmodule/self_check/self_check.c
index 2c5b363..1bbefa9 100644
--- a/src/crypto/fipsmodule/self_check/self_check.c
+++ b/src/crypto/fipsmodule/self_check/self_check.c
@@ -394,6 +394,7 @@
   // AES-CBC Encryption KAT
   memcpy(aes_iv, kAESIV, sizeof(kAESIV));
   if (AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) != 0) {
+    fprintf(stderr, "AES_set_encrypt_key failed.\n");
     goto err;
   }
   AES_cbc_encrypt(kPlaintext, output, sizeof(kPlaintext), &aes_key, aes_iv,
@@ -406,6 +407,7 @@
   // AES-CBC Decryption KAT
   memcpy(aes_iv, kAESIV, sizeof(kAESIV));
   if (AES_set_decrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) != 0) {
+    fprintf(stderr, "AES_set_decrypt_key failed.\n");
     goto err;
   }
   AES_cbc_encrypt(kAESCBCCiphertext, output, sizeof(kAESCBCCiphertext),
@@ -420,6 +422,7 @@
   OPENSSL_memset(nonce, 0, sizeof(nonce));
   if (!EVP_AEAD_CTX_init(&aead_ctx, EVP_aead_aes_128_gcm(), kAESKey,
                          sizeof(kAESKey), 0, NULL)) {
+    fprintf(stderr, "EVP_AEAD_CTX_init for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -429,6 +432,7 @@
                          kPlaintext, sizeof(kPlaintext), NULL, 0) ||
       !check_test(kAESGCMCiphertext, output, sizeof(kAESGCMCiphertext),
                   "AES-GCM Encryption KAT")) {
+    fprintf(stderr, "EVP_AEAD_CTX_seal for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -439,6 +443,7 @@
                          0) ||
       !check_test(kPlaintext, output, sizeof(kPlaintext),
                   "AES-GCM Decryption KAT")) {
+    fprintf(stderr, "EVP_AEAD_CTX_open for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -504,6 +509,7 @@
                 &sig_len, rsa_key) ||
       !check_test(kRSASignature, output, sizeof(kRSASignature),
                   "RSA Sign KAT")) {
+    fprintf(stderr, "RSA signing test failed.\n");
     goto err;
   }
 
@@ -558,6 +564,7 @@
                          sizeof(kDRBGAD)) ||
       !check_test(kDRBGReseedOutput, output, sizeof(kDRBGReseedOutput),
                   "DRBG Reseed KAT")) {
+    fprintf(stderr, "CTR-DRBG failed.\n");
     goto err;
   }
   CTR_DRBG_clear(&drbg);
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
index cec3669..80567d9 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
@@ -330,6 +330,7 @@
 .asciz	"SHA1 block transform for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
 .align	2
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 ___
 }}}
 
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
index e3244f7..22c47d7 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
@@ -428,6 +428,7 @@
 $code.=<<___;
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 ___
 
diff --git a/src/crypto/hkdf/CMakeLists.txt b/src/crypto/hkdf/CMakeLists.txt
deleted file mode 100644
index 43309eb..0000000
--- a/src/crypto/hkdf/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  hkdf
-
-  OBJECT
-
-  hkdf.c
-)
diff --git a/src/crypto/internal.h b/src/crypto/internal.h
index c4e2e51..6944d56 100644
--- a/src/crypto/internal.h
+++ b/src/crypto/internal.h
@@ -132,13 +132,13 @@
 #endif
 #endif
 
-#if !defined(OPENSSL_NO_THREADS) && \
+#if defined(OPENSSL_THREADS) && \
     (!defined(OPENSSL_WINDOWS) || defined(__MINGW32__))
 #include <pthread.h>
 #define OPENSSL_PTHREADS
 #endif
 
-#if !defined(OPENSSL_NO_THREADS) && !defined(OPENSSL_PTHREADS) && \
+#if defined(OPENSSL_THREADS) && !defined(OPENSSL_PTHREADS) && \
     defined(OPENSSL_WINDOWS)
 #define OPENSSL_WINDOWS_THREADS
 OPENSSL_MSVC_PRAGMA(warning(push, 3))
@@ -367,7 +367,7 @@
 
 // Thread-safe initialisation.
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 typedef uint32_t CRYPTO_once_t;
 #define CRYPTO_ONCE_INIT 0
 #elif defined(OPENSSL_WINDOWS_THREADS)
@@ -423,7 +423,7 @@
 // thread.h as a structure large enough to fit the real type. The global lock is
 // a different type so it may be initialized with platform initializer macros.
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 struct CRYPTO_STATIC_MUTEX {
   char padding;  // Empty structs have different sizes in C and C++.
 };
@@ -488,7 +488,7 @@
 #if defined(__cplusplus)
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 namespace internal {
 
@@ -516,7 +516,7 @@
 using MutexReadLock =
     internal::MutexLockBase<CRYPTO_MUTEX_lock_read, CRYPTO_MUTEX_unlock_read>;
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern "C++"
 #endif  // defined(__cplusplus)
diff --git a/src/crypto/lhash/CMakeLists.txt b/src/crypto/lhash/CMakeLists.txt
deleted file mode 100644
index 7a5f161..0000000
--- a/src/crypto/lhash/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  lhash
-
-  OBJECT
-
-  lhash.c
-)
diff --git a/src/crypto/mem.c b/src/crypto/mem.c
index 5d45baa..a06061b 100644
--- a/src/crypto/mem.c
+++ b/src/crypto/mem.c
@@ -71,6 +71,25 @@
 
 #define OPENSSL_MALLOC_PREFIX 8
 
+#if defined(__GNUC__) || defined(__clang__)
+// sdallocx is a sized |free| function. By passing the size (which we happen to
+// always know in BoringSSL), the malloc implementation can save work. We cannot
+// depend on |sdallocx| being available so we declare a wrapper that falls back
+// to |free| as a weak symbol.
+//
+// This will always be safe, but will only be overridden if the malloc
+// implementation is statically linked with BoringSSL. So, if |sdallocx| is
+// provided in, say, libc.so, we still won't use it because that's dynamically
+// linked. This isn't an ideal result, but its helps in some cases.
+void sdallocx(void *ptr, size_t size, int flags);
+
+__attribute((weak, noinline))
+#else
+static
+#endif
+void sdallocx(void *ptr, size_t size, int flags) {
+  free(ptr);
+}
 
 void *OPENSSL_malloc(size_t size) {
   void *ptr = malloc(size + OPENSSL_MALLOC_PREFIX);
@@ -92,7 +111,7 @@
 
   size_t size = *(size_t *)ptr;
   OPENSSL_cleanse(ptr, size + OPENSSL_MALLOC_PREFIX);
-  free(ptr);
+  sdallocx(ptr, size + OPENSSL_MALLOC_PREFIX, 0 /* flags */);
 }
 
 void *OPENSSL_realloc(void *orig_ptr, size_t new_size) {
diff --git a/src/crypto/obj/CMakeLists.txt b/src/crypto/obj/CMakeLists.txt
deleted file mode 100644
index b8a4ef3..0000000
--- a/src/crypto/obj/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  obj
-
-  OBJECT
-
-  obj.c
-  obj_xref.c
-)
diff --git a/src/crypto/pem/CMakeLists.txt b/src/crypto/pem/CMakeLists.txt
deleted file mode 100644
index 30dd7c9..0000000
--- a/src/crypto/pem/CMakeLists.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  pem
-
-  OBJECT
-
-  pem_all.c
-  pem_info.c
-  pem_lib.c
-  pem_oth.c
-  pem_pk8.c
-  pem_pkey.c
-  pem_x509.c
-  pem_xaux.c
-)
diff --git a/src/crypto/pem/pem_info.c b/src/crypto/pem/pem_info.c
index d707e42..3627a45 100644
--- a/src/crypto/pem/pem_info.c
+++ b/src/crypto/pem/pem_info.c
@@ -75,221 +75,203 @@
 STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,
                                         pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    STACK_OF(X509_INFO) *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_X509_INFO_read_bio(b, sk, cb, u);
+    STACK_OF(X509_INFO) *ret = PEM_X509_INFO_read_bio(b, sk, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
+enum parse_result_t {
+    parse_ok,
+    parse_error,
+    parse_new_entry,
+};
+
+static enum parse_result_t parse_x509(X509_INFO *info, const uint8_t *data,
+                                      size_t len, int key_type)
+{
+    if (info->x509 != NULL) {
+        return parse_new_entry;
+    }
+    info->x509 = d2i_X509(NULL, &data, len);
+    return info->x509 != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_x509_aux(X509_INFO *info, const uint8_t *data,
+                                          size_t len, int key_type)
+{
+    if (info->x509 != NULL) {
+        return parse_new_entry;
+    }
+    info->x509 = d2i_X509_AUX(NULL, &data, len);
+    return info->x509 != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_crl(X509_INFO *info, const uint8_t *data,
+                                     size_t len, int key_type)
+{
+    if (info->crl != NULL) {
+        return parse_new_entry;
+    }
+    info->crl = d2i_X509_CRL(NULL, &data, len);
+    return info->crl != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_key(X509_INFO *info, const uint8_t *data,
+                                     size_t len, int key_type)
+{
+    if (info->x_pkey != NULL) {
+        return parse_new_entry;
+    }
+    info->x_pkey = X509_PKEY_new();
+    if (info->x_pkey == NULL) {
+        return parse_error;
+    }
+    info->x_pkey->dec_pkey = d2i_PrivateKey(key_type, NULL, &data, len);
+    return info->x_pkey->dec_pkey != NULL ? parse_ok : parse_error;
+}
+
 STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk,
                                             pem_password_cb *cb, void *u)
 {
-    X509_INFO *xi = NULL;
+    X509_INFO *info = NULL;
     char *name = NULL, *header = NULL;
-    void *pp;
     unsigned char *data = NULL;
-    const unsigned char *p;
-    long len, error = 0;
+    long len;
     int ok = 0;
     STACK_OF(X509_INFO) *ret = NULL;
-    unsigned int i, raw, ptype;
-    d2i_of_void *d2i = 0;
 
     if (sk == NULL) {
-        if ((ret = sk_X509_INFO_new_null()) == NULL) {
+        ret = sk_X509_INFO_new_null();
+        if (ret == NULL) {
             OPENSSL_PUT_ERROR(PEM, ERR_R_MALLOC_FAILURE);
-            goto err;
+            return NULL;
         }
-    } else
+    } else {
         ret = sk;
+    }
+    size_t orig_num = sk_X509_INFO_num(ret);
 
-    if ((xi = X509_INFO_new()) == NULL)
+    info = X509_INFO_new();
+    if (info == NULL) {
         goto err;
+    }
+
     for (;;) {
-        raw = 0;
-        ptype = 0;
-        i = PEM_read_bio(bp, &name, &header, &data, &len);
-        if (i == 0) {
-            error = ERR_GET_REASON(ERR_peek_last_error());
-            if (error == PEM_R_NO_START_LINE) {
+        if (!PEM_read_bio(bp, &name, &header, &data, &len)) {
+            uint32_t error = ERR_peek_last_error();
+            if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                ERR_GET_REASON(error) == PEM_R_NO_START_LINE) {
                 ERR_clear_error();
                 break;
             }
             goto err;
         }
- start:
-        if ((strcmp(name, PEM_STRING_X509) == 0) ||
-            (strcmp(name, PEM_STRING_X509_OLD) == 0)) {
-            d2i = (D2I_OF(void)) d2i_X509;
-            if (xi->x509 != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->x509);
-        } else if ((strcmp(name, PEM_STRING_X509_TRUSTED) == 0)) {
-            d2i = (D2I_OF(void)) d2i_X509_AUX;
-            if (xi->x509 != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->x509);
+
+        enum parse_result_t (*parse_function)(X509_INFO *, const uint8_t *,
+                                              size_t, int) = NULL;
+        int key_type = EVP_PKEY_NONE;
+        if (strcmp(name, PEM_STRING_X509) == 0 ||
+            strcmp(name, PEM_STRING_X509_OLD) == 0) {
+            parse_function = parse_x509;
+        } else if (strcmp(name, PEM_STRING_X509_TRUSTED) == 0) {
+            parse_function = parse_x509_aux;
         } else if (strcmp(name, PEM_STRING_X509_CRL) == 0) {
-            d2i = (D2I_OF(void)) d2i_X509_CRL;
-            if (xi->crl != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->crl);
+            parse_function = parse_crl;
         } else if (strcmp(name, PEM_STRING_RSA) == 0) {
-            d2i = (D2I_OF(void)) d2i_RSAPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_RSA;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else
-#ifndef OPENSSL_NO_DSA
-        if (strcmp(name, PEM_STRING_DSA) == 0) {
-            d2i = (D2I_OF(void)) d2i_DSAPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_DSA;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else
-#endif
-        if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
-            d2i = (D2I_OF(void)) d2i_ECPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_EC;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else {
-            d2i = NULL;
-            pp = NULL;
+            parse_function = parse_key;
+            key_type = EVP_PKEY_RSA;
+        } else if (strcmp(name, PEM_STRING_DSA) == 0) {
+            parse_function = parse_key;
+            key_type = EVP_PKEY_DSA;
+        } else if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
+            parse_function = parse_key;
+            key_type = EVP_PKEY_EC;
         }
 
-        if (d2i != NULL) {
-            if (!raw) {
-                EVP_CIPHER_INFO cipher;
-
-                if (!PEM_get_EVP_CIPHER_INFO(header, &cipher))
-                    goto err;
-                if (!PEM_do_header(&cipher, data, &len, cb, u))
-                    goto err;
-                p = data;
-                if (ptype) {
-                    if (!d2i_PrivateKey(ptype, pp, &p, len)) {
-                        OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
-                        goto err;
-                    }
-                } else if (d2i(pp, &p, len) == NULL) {
-                    OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
+        /* If a private key has a header, assume it is encrypted. */
+        if (key_type != EVP_PKEY_NONE && strlen(header) > 10) {
+            if (info->x_pkey != NULL) {
+                if (!sk_X509_INFO_push(ret, info)) {
                     goto err;
                 }
-            } else {            /* encrypted RSA data */
-                if (!PEM_get_EVP_CIPHER_INFO(header, &xi->enc_cipher))
+                info = X509_INFO_new();
+                if (info == NULL) {
                     goto err;
-                xi->enc_data = (char *)data;
-                xi->enc_len = (int)len;
-                data = NULL;
+                }
             }
-        } else {
-            /* unknown */
+            /* Historically, raw entries pushed an empty key. */
+            info->x_pkey = X509_PKEY_new();
+            if (info->x_pkey == NULL ||
+                !PEM_get_EVP_CIPHER_INFO(header, &info->enc_cipher)) {
+                goto err;
+            }
+            info->enc_data = (char *)data;
+            info->enc_len = (int)len;
+            data = NULL;
+        } else if (parse_function != NULL) {
+            EVP_CIPHER_INFO cipher;
+            if (!PEM_get_EVP_CIPHER_INFO(header, &cipher) ||
+                !PEM_do_header(&cipher, data, &len, cb, u)) {
+                goto err;
+            }
+            enum parse_result_t result =
+                parse_function(info, data, len, key_type);
+            if (result == parse_new_entry) {
+                if (!sk_X509_INFO_push(ret, info)) {
+                    goto err;
+                }
+                info = X509_INFO_new();
+                if (info == NULL) {
+                    goto err;
+                }
+                result = parse_function(info, data, len, key_type);
+            }
+            if (result != parse_ok) {
+                OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
+                goto err;
+            }
         }
-        if (name != NULL)
-            OPENSSL_free(name);
-        if (header != NULL)
-            OPENSSL_free(header);
-        if (data != NULL)
-            OPENSSL_free(data);
+        OPENSSL_free(name);
+        OPENSSL_free(header);
+        OPENSSL_free(data);
         name = NULL;
         header = NULL;
         data = NULL;
     }
 
-    /*
-     * if the last one hasn't been pushed yet and there is anything in it
-     * then add it to the stack ...
-     */
-    if ((xi->x509 != NULL) || (xi->crl != NULL) ||
-        (xi->x_pkey != NULL) || (xi->enc_data != NULL)) {
-        if (!sk_X509_INFO_push(ret, xi))
+    /* Push the last entry on the stack if not empty. */
+    if (info->x509 != NULL || info->crl != NULL ||
+        info->x_pkey != NULL || info->enc_data != NULL) {
+        if (!sk_X509_INFO_push(ret, info)) {
             goto err;
-        xi = NULL;
-    }
-    ok = 1;
- err:
-    if (xi != NULL)
-        X509_INFO_free(xi);
-    if (!ok) {
-        for (i = 0; i < sk_X509_INFO_num(ret); i++) {
-            xi = sk_X509_INFO_value(ret, i);
-            X509_INFO_free(xi);
         }
-        if (ret != sk)
+        info = NULL;
+    }
+
+    ok = 1;
+
+ err:
+    X509_INFO_free(info);
+    if (!ok) {
+        while (sk_X509_INFO_num(ret) > orig_num) {
+            X509_INFO_free(sk_X509_INFO_pop(ret));
+        }
+        if (ret != sk) {
             sk_X509_INFO_free(ret);
+        }
         ret = NULL;
     }
 
-    if (name != NULL)
-        OPENSSL_free(name);
-    if (header != NULL)
-        OPENSSL_free(header);
-    if (data != NULL)
-        OPENSSL_free(data);
-    return (ret);
+    OPENSSL_free(name);
+    OPENSSL_free(header);
+    OPENSSL_free(data);
+    return ret;
 }
 
 /* A TJH addition */
diff --git a/src/crypto/pem/pem_lib.c b/src/crypto/pem/pem_lib.c
index 5180e55..c682429 100644
--- a/src/crypto/pem/pem_lib.c
+++ b/src/crypto/pem/pem_lib.c
@@ -121,17 +121,14 @@
 void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
                     pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    void *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_ASN1_read_bio(d2i, name, b, x, cb, u);
+    void *ret = PEM_ASN1_read_bio(d2i, name, b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
@@ -220,8 +217,11 @@
 
     for (;;) {
         if (!PEM_read_bio(bp, &nm, &header, &data, &len)) {
-            if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE)
+            uint32_t error = ERR_peek_error();
+            if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                ERR_GET_REASON(error) == PEM_R_NO_START_LINE) {
                 ERR_add_error_data(2, "Expecting: ", name);
+            }
             return 0;
         }
         if (check_pem(nm, name))
@@ -257,17 +257,14 @@
                    void *x, const EVP_CIPHER *enc, unsigned char *kstr,
                    int klen, pem_password_cb *callback, void *u)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_ASN1_write_bio(i2d, name, b, x, enc, kstr, klen, callback, u);
+    int ret = PEM_ASN1_write_bio(i2d, name, b, x, enc, kstr, klen, callback, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
@@ -514,15 +511,12 @@
 int PEM_write(FILE *fp, const char *name, const char *header,
               const unsigned char *data, long len)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_write_bio(b, name, header, data, len);
+    int ret = PEM_write_bio(b, name, header, data, len);
     BIO_free(b);
     return (ret);
 }
@@ -588,15 +582,12 @@
 int PEM_read(FILE *fp, char **name, char **header, unsigned char **data,
              long *len)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio(b, name, header, data, len);
+    int ret = PEM_read_bio(b, name, header, data, len);
     BIO_free(b);
     return (ret);
 }
diff --git a/src/crypto/pem/pem_pkey.c b/src/crypto/pem/pem_pkey.c
index 9fbaeef..725a84b 100644
--- a/src/crypto/pem/pem_pkey.c
+++ b/src/crypto/pem/pem_pkey.c
@@ -155,31 +155,26 @@
 EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
                               void *u)
 {
-    BIO *b;
-    EVP_PKEY *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio_PrivateKey(b, x, cb, u);
+    EVP_PKEY *ret = PEM_read_bio_PrivateKey(b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
                          unsigned char *kstr, int klen,
                          pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
         return 0;
     }
-    ret = PEM_write_bio_PrivateKey(b, x, enc, kstr, klen, cb, u);
+    int ret = PEM_write_bio_PrivateKey(b, x, enc, kstr, klen, cb, u);
     BIO_free(b);
     return ret;
 }
@@ -212,16 +207,13 @@
 #ifndef OPENSSL_NO_FP_API
 DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    DH *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio_DHparams(b, x, cb, u);
+    DH *ret = PEM_read_bio_DHparams(b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
diff --git a/src/crypto/perlasm/arm-xlate.pl b/src/crypto/perlasm/arm-xlate.pl
index 3bbcd5d..29e086d 100755
--- a/src/crypto/perlasm/arm-xlate.pl
+++ b/src/crypto/perlasm/arm-xlate.pl
@@ -143,6 +143,10 @@
 print "#if defined(__arm__)\n" if ($flavour eq "linux32");
 print "#if defined(__aarch64__)\n" if ($flavour eq "linux64");
 
+print "#if defined(BORINGSSL_PREFIX)\n";
+print "#include <boringssl_prefix_symbols_asm.h>\n";
+print "#endif\n";
+
 while(my $line=<>) {
 
     if ($line =~ m/^\s*(#|@|\/\/)/)	{ print $line; next; }
@@ -190,6 +194,6 @@
 }
 
 print "#endif\n" if ($flavour eq "linux32" || $flavour eq "linux64");
-print "#endif  // !OPENSSL_NO_ASM";
+print "#endif  // !OPENSSL_NO_ASM\n";
 
 close STDOUT;
diff --git a/src/crypto/perlasm/ppc-xlate.pl b/src/crypto/perlasm/ppc-xlate.pl
index 8013058..7f01dbd 100644
--- a/src/crypto/perlasm/ppc-xlate.pl
+++ b/src/crypto/perlasm/ppc-xlate.pl
@@ -257,8 +257,8 @@
 
 print <<___;
 #if defined(__has_feature)
-#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)"
-#define OPENSSL_NO_ASM"
+#if __has_feature(memory_sanitizer) && !defined(OPENSSL_NO_ASM)
+#define OPENSSL_NO_ASM
 #endif
 #endif
 
@@ -306,6 +306,6 @@
     print "\n";
 }
 
-print "#endif  // !OPENSSL_NO_ASM && __powerpc64__";
+print "#endif  // !OPENSSL_NO_ASM && __powerpc64__\n";
 
 close STDOUT;
diff --git a/src/crypto/perlasm/x86_64-xlate.pl b/src/crypto/perlasm/x86_64-xlate.pl
index 10041d3..3ec9b6c 100755
--- a/src/crypto/perlasm/x86_64-xlate.pl
+++ b/src/crypto/perlasm/x86_64-xlate.pl
@@ -1129,6 +1129,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 ___
 } elsif ($masm) {
     print <<___;
@@ -1145,6 +1149,9 @@
 #endif
 
 #if defined(__x86_64__) && !defined(OPENSSL_NO_ASM)
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
 ___
 }
 
diff --git a/src/crypto/pkcs7/CMakeLists.txt b/src/crypto/pkcs7/CMakeLists.txt
deleted file mode 100644
index 65c6566..0000000
--- a/src/crypto/pkcs7/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  pkcs7
-
-  OBJECT
-
-  pkcs7.c
-  pkcs7_x509.c
-)
diff --git a/src/crypto/pkcs7/pkcs7_x509.c b/src/crypto/pkcs7/pkcs7_x509.c
index a2a6b46..d6ca44e 100644
--- a/src/crypto/pkcs7/pkcs7_x509.c
+++ b/src/crypto/pkcs7/pkcs7_x509.c
@@ -333,17 +333,7 @@
 }
 
 int i2d_PKCS7_bio(BIO *bio, const PKCS7 *p7) {
-  size_t written = 0;
-  while (written < p7->ber_len) {
-    size_t todo = p7->ber_len - written;
-    int len = todo > INT_MAX ? INT_MAX : (int)todo;
-    int ret = BIO_write(bio, p7->ber_bytes + written, len);
-    if (ret <= 0) {
-      return 0;
-    }
-    written += (size_t)ret;
-  }
-  return 1;
+  return BIO_write_all(bio, p7->ber_bytes, p7->ber_len);
 }
 
 void PKCS7_free(PKCS7 *p7) {
diff --git a/src/crypto/pkcs8/CMakeLists.txt b/src/crypto/pkcs8/CMakeLists.txt
deleted file mode 100644
index 417dce3..0000000
--- a/src/crypto/pkcs8/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  pkcs8_lib
-
-  OBJECT
-
-  pkcs8.c
-  pkcs8_x509.c
-  p5_pbev2.c
-)
diff --git a/src/crypto/pkcs8/pkcs8_x509.c b/src/crypto/pkcs8/pkcs8_x509.c
index dc74d96..2c7841e 100644
--- a/src/crypto/pkcs8/pkcs8_x509.c
+++ b/src/crypto/pkcs8/pkcs8_x509.c
@@ -861,17 +861,7 @@
 }
 
 int i2d_PKCS12_bio(BIO *bio, const PKCS12 *p12) {
-  size_t written = 0;
-  while (written < p12->ber_len) {
-    size_t todo = p12->ber_len - written;
-    int len = todo > INT_MAX ? INT_MAX : (int)todo;
-    int ret = BIO_write(bio, p12->ber_bytes + written, len);
-    if (ret <= 0) {
-      return 0;
-    }
-    written += (size_t)ret;
-  }
-  return 1;
+  return BIO_write_all(bio, p12->ber_bytes, p12->ber_len);
 }
 
 int i2d_PKCS12_fp(FILE *fp, const PKCS12 *p12) {
diff --git a/src/crypto/poly1305/CMakeLists.txt b/src/crypto/poly1305/CMakeLists.txt
deleted file mode 100644
index 5dc1b19..0000000
--- a/src/crypto/poly1305/CMakeLists.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-include_directories(../../include)
-
-if(${ARCH} STREQUAL "arm")
-  set(
-    POLY1305_ARCH_SOURCES
-
-    poly1305_arm_asm.S
-  )
-endif()
-
-add_library(
-  poly1305
-
-  OBJECT
-
-  poly1305.c
-  poly1305_arm.c
-  poly1305_vec.c
-
-  ${POLY1305_ARCH_SOURCES}
-)
diff --git a/src/crypto/poly1305/poly1305_arm_asm.S b/src/crypto/poly1305/poly1305_arm_asm.S
index db6b251..04f7c4c 100644
--- a/src/crypto/poly1305/poly1305_arm_asm.S
+++ b/src/crypto/poly1305/poly1305_arm_asm.S
@@ -6,6 +6,10 @@
 
 #if defined(__arm__) && !defined(OPENSSL_NO_ASM) && !defined(__APPLE__)
 
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols_asm.h>
+#endif
+
 # This implementation was taken from the public domain, neon2 version in
 # SUPERCOP by D. J. Bernstein and Peter Schwabe.
 
diff --git a/src/crypto/pool/CMakeLists.txt b/src/crypto/pool/CMakeLists.txt
deleted file mode 100644
index 6f2c784..0000000
--- a/src/crypto/pool/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  pool
-
-  OBJECT
-
-  pool.c
-)
diff --git a/src/crypto/pool/pool_test.cc b/src/crypto/pool/pool_test.cc
index 62fb17d..8f32fb6 100644
--- a/src/crypto/pool/pool_test.cc
+++ b/src/crypto/pool/pool_test.cc
@@ -18,7 +18,7 @@
 
 #include "../test/test_util.h"
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <chrono>
 #include <thread>
 #endif
@@ -61,7 +61,7 @@
   EXPECT_EQ(buf.get(), buf2.get()) << "CRYPTO_BUFFER_POOL did not dedup data.";
 }
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 TEST(PoolTest, Threads) {
   bssl::UniquePtr<CRYPTO_BUFFER_POOL> pool(CRYPTO_BUFFER_POOL_new());
   ASSERT_TRUE(pool);
diff --git a/src/crypto/rand_extra/CMakeLists.txt b/src/crypto/rand_extra/CMakeLists.txt
deleted file mode 100644
index cdd7aa8..0000000
--- a/src/crypto/rand_extra/CMakeLists.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  rand_extra
-
-  OBJECT
-
-  deterministic.c
-  forkunsafe.c
-  fuchsia.c
-  rand_extra.c
-  windows.c
-)
diff --git a/src/crypto/rand_extra/rand_test.cc b/src/crypto/rand_extra/rand_test.cc
new file mode 100644
index 0000000..d0a7e40
--- /dev/null
+++ b/src/crypto/rand_extra/rand_test.cc
@@ -0,0 +1,186 @@
+/* Copyright (c) 2018, 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. */
+
+#include <openssl/rand.h>
+
+#include <gtest/gtest.h>
+
+#include <openssl/span.h>
+
+#include "../test/test_util.h"
+
+#if defined(OPENSSL_THREADS)
+#include <array>
+#include <thread>
+#include <vector>
+#endif
+
+#if !defined(OPENSSL_WINDOWS)
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
+
+// These tests are, strictly speaking, flaky, but we use large enough buffers
+// that the probability of failing when we should pass is negligible.
+
+TEST(RandTest, NotObviouslyBroken) {
+  static const uint8_t kZeros[256] = {0};
+
+  uint8_t buf1[256], buf2[256];
+  RAND_bytes(buf1, sizeof(buf1));
+  RAND_bytes(buf2, sizeof(buf2));
+
+  EXPECT_NE(Bytes(buf1), Bytes(buf2));
+  EXPECT_NE(Bytes(buf1), Bytes(kZeros));
+  EXPECT_NE(Bytes(buf2), Bytes(kZeros));
+}
+
+#if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_IOS) && \
+    !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+static bool ForkAndRand(bssl::Span<uint8_t> out) {
+  int pipefds[2];
+  if (pipe(pipefds) < 0) {
+    perror("pipe");
+    return false;
+  }
+
+  // This is a multi-threaded process, but GTest does not run tests concurrently
+  // and there currently are no threads, so this should be safe.
+  pid_t child = fork();
+  if (child < 0) {
+    perror("fork");
+    close(pipefds[0]);
+    close(pipefds[1]);
+    return false;
+  }
+
+  if (child == 0) {
+    // This is the child. Generate entropy and write it to the parent.
+    close(pipefds[0]);
+    RAND_bytes(out.data(), out.size());
+    while (!out.empty()) {
+      ssize_t ret = write(pipefds[1], out.data(), out.size());
+      if (ret < 0) {
+        if (errno == EINTR) {
+          continue;
+        }
+        perror("write");
+        _exit(1);
+      }
+      out = out.subspan(static_cast<size_t>(ret));
+    }
+    _exit(0);
+  }
+
+  // This is the parent. Read the entropy from the child.
+  close(pipefds[1]);
+  while (!out.empty()) {
+    ssize_t ret = read(pipefds[0], out.data(), out.size());
+    if (ret <= 0) {
+      if (ret == 0) {
+        fprintf(stderr, "Unexpected EOF from child.\n");
+      } else {
+        if (errno == EINTR) {
+          continue;
+        }
+        perror("read");
+      }
+      close(pipefds[0]);
+      return false;
+    }
+    out = out.subspan(static_cast<size_t>(ret));
+  }
+  close(pipefds[0]);
+
+  // Wait for the child to exit.
+  int status;
+  if (waitpid(child, &status, 0) < 0) {
+    perror("waitpid");
+    return false;
+  }
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+    fprintf(stderr, "Child did not exit cleanly.\n");
+    return false;
+  }
+
+  return true;
+}
+
+TEST(RandTest, Fork) {
+  static const uint8_t kZeros[16] = {0};
+
+  // Draw a little entropy to initialize any internal PRNG buffering.
+  uint8_t byte;
+  RAND_bytes(&byte, 1);
+
+  // Draw entropy in two child processes and the parent process. This test
+  // intentionally uses smaller buffers than the others, to minimize the chance
+  // of sneaking by with a large enough buffer that we've since reseeded from
+  // the OS.
+  uint8_t buf1[16], buf2[16], buf3[16];
+  ASSERT_TRUE(ForkAndRand(buf1));
+  ASSERT_TRUE(ForkAndRand(buf2));
+  RAND_bytes(buf3, sizeof(buf3));
+
+  // All should be different.
+  EXPECT_NE(Bytes(buf1), Bytes(buf2));
+  EXPECT_NE(Bytes(buf2), Bytes(buf3));
+  EXPECT_NE(Bytes(buf1), Bytes(buf3));
+  EXPECT_NE(Bytes(buf1), Bytes(kZeros));
+  EXPECT_NE(Bytes(buf2), Bytes(kZeros));
+  EXPECT_NE(Bytes(buf3), Bytes(kZeros));
+}
+#endif  // !OPENSSL_WINDOWS && !OPENSSL_IOS &&
+        // !BORINGSSL_UNSAFE_DETERMINISTIC_MODE
+
+#if defined(OPENSSL_THREADS)
+static void RunConcurrentRands(size_t num_threads) {
+  static const uint8_t kZeros[256] = {0};
+
+  std::vector<std::array<uint8_t, 256>> bufs(num_threads);
+  std::vector<std::thread> threads(num_threads);
+
+  for (size_t i = 0; i < num_threads; i++) {
+    threads[i] =
+        std::thread([i, &bufs] { RAND_bytes(bufs[i].data(), bufs[i].size()); });
+  }
+  for (size_t i = 0; i < num_threads; i++) {
+    threads[i].join();
+  }
+
+  for (size_t i = 0; i < num_threads; i++) {
+    EXPECT_NE(Bytes(bufs[i]), Bytes(kZeros));
+    for (size_t j = i + 1; j < num_threads; j++) {
+      EXPECT_NE(Bytes(bufs[i]), Bytes(bufs[j]));
+    }
+  }
+}
+
+// Test that threads may concurrently draw entropy without tripping TSan.
+TEST(RandTest, Threads) {
+  constexpr size_t kFewerThreads = 10;
+  constexpr size_t kMoreThreads = 20;
+
+  // Draw entropy in parallel.
+  RunConcurrentRands(kFewerThreads);
+  // Draw entropy in parallel with higher concurrency than the previous maximum.
+  RunConcurrentRands(kMoreThreads);
+  // Draw entropy in parallel with lower concurrency than the previous maximum.
+  RunConcurrentRands(kFewerThreads);
+}
+#endif
diff --git a/src/crypto/rc4/CMakeLists.txt b/src/crypto/rc4/CMakeLists.txt
deleted file mode 100644
index a008fe5..0000000
--- a/src/crypto/rc4/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  rc4
-
-  OBJECT
-
-  rc4.c
-)
diff --git a/src/crypto/refcount_test.cc b/src/crypto/refcount_test.cc
index 6ce0746..700863f 100644
--- a/src/crypto/refcount_test.cc
+++ b/src/crypto/refcount_test.cc
@@ -16,7 +16,7 @@
 
 #include <gtest/gtest.h>
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <thread>
 #endif
 
@@ -43,7 +43,7 @@
   EXPECT_EQ(1u, count);
 }
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 // This test is primarily intended to run under ThreadSanitizer.
 TEST(RefCountTest, Threads) {
   CRYPTO_refcount_t count = 0;
diff --git a/src/crypto/rsa_extra/CMakeLists.txt b/src/crypto/rsa_extra/CMakeLists.txt
deleted file mode 100644
index b705f16..0000000
--- a/src/crypto/rsa_extra/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  rsa_extra
-
-  OBJECT
-
-  rsa_asn1.c
-  rsa_print.c
-)
diff --git a/src/crypto/rsa_extra/rsa_test.cc b/src/crypto/rsa_extra/rsa_test.cc
index b0a0b7e..9bd47b1 100644
--- a/src/crypto/rsa_extra/rsa_test.cc
+++ b/src/crypto/rsa_extra/rsa_test.cc
@@ -72,7 +72,7 @@
 #include "../internal.h"
 #include "../test/test_util.h"
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <thread>
 #include <vector>
 #endif
@@ -1048,7 +1048,7 @@
 }
 #endif  // !BORINGSSL_SHARED_LIBRARY
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 TEST(RSATest, Threads) {
   bssl::UniquePtr<RSA> rsa_template(
       RSA_private_key_from_bytes(kKey1, sizeof(kKey1) - 1));
diff --git a/src/crypto/stack/CMakeLists.txt b/src/crypto/stack/CMakeLists.txt
deleted file mode 100644
index dcd8ef4..0000000
--- a/src/crypto/stack/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  stack
-
-  OBJECT
-
-  stack.c
-)
diff --git a/src/crypto/stack/stack.c b/src/crypto/stack/stack.c
index 7aa3218..93b9d1b 100644
--- a/src/crypto/stack/stack.c
+++ b/src/crypto/stack/stack.c
@@ -133,19 +133,31 @@
   OPENSSL_free(sk);
 }
 
-void sk_pop_free(_STACK *sk, void (*func)(void *)) {
+void sk_pop_free_ex(_STACK *sk, void (*call_free_func)(stack_free_func, void *),
+                    stack_free_func free_func) {
   if (sk == NULL) {
     return;
   }
 
   for (size_t i = 0; i < sk->num; i++) {
     if (sk->data[i] != NULL) {
-      func(sk->data[i]);
+      call_free_func(free_func, sk->data[i]);
     }
   }
   sk_free(sk);
 }
 
+// Historically, |sk_pop_free| called the function as |stack_free_func|
+// directly. This is undefined in C. Some callers called |sk_pop_free| directly,
+// so we must maintain a compatibility version for now.
+static void call_free_func_legacy(stack_free_func func, void *ptr) {
+  func(ptr);
+}
+
+void sk_pop_free(_STACK *sk, stack_free_func free_func) {
+  sk_pop_free_ex(sk, call_free_func_legacy, free_func);
+}
+
 size_t sk_insert(_STACK *sk, void *p, size_t where) {
   if (sk == NULL) {
     return 0;
@@ -209,7 +221,7 @@
   return ret;
 }
 
-void *sk_delete_ptr(_STACK *sk, void *p) {
+void *sk_delete_ptr(_STACK *sk, const void *p) {
   if (sk == NULL) {
     return NULL;
   }
@@ -223,7 +235,9 @@
   return NULL;
 }
 
-int sk_find(const _STACK *sk, size_t *out_index, void *p) {
+int sk_find(const _STACK *sk, size_t *out_index, const void *p,
+            int (*call_cmp_func)(stack_cmp_func, const void **,
+                                 const void **)) {
   if (sk == NULL) {
     return 0;
   }
@@ -247,7 +261,8 @@
 
   if (!sk_is_sorted(sk)) {
     for (size_t i = 0; i < sk->num; i++) {
-      if (sk->comp((const void **)&p, (const void **)&sk->data[i]) == 0) {
+      const void *elem = sk->data[i];
+      if (call_cmp_func(sk->comp, &p, &elem) == 0) {
         if (out_index) {
           *out_index = i;
         }
@@ -262,15 +277,25 @@
   // elements. However, since we're passing an array of pointers to
   // qsort/bsearch, we can just cast the comparison function and everything
   // works.
+  //
+  // TODO(davidben): This is undefined behavior, but the call is in libc so,
+  // e.g., CFI does not notice. Unfortunately, |bsearch| is missing a void*
+  // parameter in its callback and |bsearch_s| is a mess of incompatibility.
   const void *const *r = bsearch(&p, sk->data, sk->num, sizeof(void *),
                                  (int (*)(const void *, const void *))sk->comp);
   if (r == NULL) {
     return 0;
   }
   size_t idx = ((void **)r) - sk->data;
-  // This function always returns the first result.
-  while (idx > 0 &&
-         sk->comp((const void **)&p, (const void **)&sk->data[idx - 1]) == 0) {
+  // This function always returns the first result. Note this logic is, in the
+  // worst case, O(N) rather than O(log(N)). If this ever becomes a problem,
+  // restore https://boringssl-review.googlesource.com/c/boringssl/+/32115/
+  // which integrates the preference into the binary search.
+  while (idx > 0) {
+    const void *elem = sk->data[idx - 1];
+    if (call_cmp_func(sk->comp, &p, &elem) != 0) {
+      break;
+    }
     idx--;
   }
   if (out_index) {
@@ -340,6 +365,11 @@
   }
 
   // See the comment in sk_find about this cast.
+  //
+  // TODO(davidben): This is undefined behavior, but the call is in libc so,
+  // e.g., CFI does not notice. Unfortunately, |qsort| is missing a void*
+  // parameter in its callback and |qsort_s| / |qsort_r| are a mess of
+  // incompatibility.
   comp_func = (int (*)(const void *, const void *))(sk->comp);
   qsort(sk->data, sk->num, sizeof(void *), comp_func);
   sk->sorted = 1;
@@ -363,8 +393,11 @@
   return old;
 }
 
-_STACK *sk_deep_copy(const _STACK *sk, void *(*copy_func)(void *),
-                     void (*free_func)(void *)) {
+_STACK *sk_deep_copy(const _STACK *sk,
+                     void *(*call_copy_func)(stack_copy_func, void *),
+                     stack_copy_func copy_func,
+                     void (*call_free_func)(stack_free_func, void *),
+                     stack_free_func free_func) {
   _STACK *ret = sk_dup(sk);
   if (ret == NULL) {
     return NULL;
@@ -374,11 +407,11 @@
     if (ret->data[i] == NULL) {
       continue;
     }
-    ret->data[i] = copy_func(ret->data[i]);
+    ret->data[i] = call_copy_func(copy_func, ret->data[i]);
     if (ret->data[i] == NULL) {
       for (size_t j = 0; j < i; j++) {
         if (ret->data[j] != NULL) {
-          free_func(ret->data[j]);
+          call_free_func(free_func, ret->data[j]);
         }
       }
       sk_free(ret);
diff --git a/src/crypto/stack/stack_test.cc b/src/crypto/stack/stack_test.cc
new file mode 100644
index 0000000..8b26971
--- /dev/null
+++ b/src/crypto/stack/stack_test.cc
@@ -0,0 +1,394 @@
+/* Copyright (c) 2018, 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. */
+
+#include <openssl/stack.h>
+
+#include <limits.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <openssl/mem.h>
+
+
+// Define a custom stack type for testing.
+using TEST_INT = int;
+
+static void TEST_INT_free(TEST_INT *x) { OPENSSL_free(x); }
+
+namespace bssl {
+BORINGSSL_MAKE_DELETER(TEST_INT, TEST_INT_free)
+}  // namespace bssl
+
+static bssl::UniquePtr<TEST_INT> TEST_INT_new(int x) {
+  bssl::UniquePtr<TEST_INT> ret(
+      static_cast<TEST_INT *>(OPENSSL_malloc(sizeof(TEST_INT))));
+  if (!ret) {
+    return nullptr;
+  }
+  *ret = x;
+  return ret;
+}
+
+DEFINE_STACK_OF(TEST_INT)
+
+struct ShallowStackDeleter {
+  void operator()(STACK_OF(TEST_INT) *sk) const { sk_TEST_INT_free(sk); }
+};
+
+using ShallowStack = std::unique_ptr<STACK_OF(TEST_INT), ShallowStackDeleter>;
+
+// kNull is treated as a nullptr expectation for purposes of ExpectStackEquals.
+// The tests in this file will never use it as a test value.
+static const int kNull = INT_MIN;
+
+static void ExpectStackEquals(const STACK_OF(TEST_INT) *sk,
+                              const std::vector<int> &vec) {
+  EXPECT_EQ(vec.size(), sk_TEST_INT_num(sk));
+  for (size_t i = 0; i < vec.size(); i++) {
+    SCOPED_TRACE(i);
+    const TEST_INT *obj = sk_TEST_INT_value(sk, i);
+    if (vec[i] == kNull) {
+      EXPECT_FALSE(obj);
+    } else {
+      EXPECT_TRUE(obj);
+      if (obj) {
+        EXPECT_EQ(vec[i], *obj);
+      }
+    }
+  }
+
+  // Reading out-of-bounds fails.
+  EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size()));
+  EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size() + 1));
+}
+
+TEST(StackTest, Basic) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null());
+  ASSERT_TRUE(sk);
+
+  // The stack starts out empty.
+  ExpectStackEquals(sk.get(), {});
+
+  // Removing elements from an empty stack does nothing.
+  EXPECT_FALSE(sk_TEST_INT_pop(sk.get()));
+  EXPECT_FALSE(sk_TEST_INT_shift(sk.get()));
+  EXPECT_FALSE(sk_TEST_INT_delete(sk.get(), 0));
+
+  // Push some elements.
+  for (int i = 0; i < 6; i++) {
+    auto value = TEST_INT_new(i);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  }
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 4, 5});
+
+  // Items may be inserted in the middle.
+  auto value = TEST_INT_new(6);
+  ASSERT_TRUE(value);
+  // Hold on to the object for later.
+  TEST_INT *raw = value.get();
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 4));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5});
+
+  // Without a comparison function, find searches by pointer.
+  value = TEST_INT_new(6);
+  ASSERT_TRUE(value);
+  size_t index;
+  EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, value.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, raw));
+  EXPECT_EQ(4u, index);
+
+  // sk_TEST_INT_insert can also insert values at the end.
+  value = TEST_INT_new(7);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 7));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7});
+
+  // Out-of-bounds indices are clamped.
+  value = TEST_INT_new(8);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 999));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7, 8});
+
+  // Test removing elements from various places.
+  bssl::UniquePtr<TEST_INT> removed(sk_TEST_INT_pop(sk.get()));
+  EXPECT_EQ(8, *removed);
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7});
+
+  removed.reset(sk_TEST_INT_shift(sk.get()));
+  EXPECT_EQ(0, *removed);
+  ExpectStackEquals(sk.get(), {1, 2, 3, 6, 4, 5, 7});
+
+  removed.reset(sk_TEST_INT_delete(sk.get(), 2));
+  EXPECT_EQ(3, *removed);
+  ExpectStackEquals(sk.get(), {1, 2, 6, 4, 5, 7});
+
+  // Objects may also be deleted by pointer.
+  removed.reset(sk_TEST_INT_delete_ptr(sk.get(), raw));
+  EXPECT_EQ(raw, removed.get());
+  ExpectStackEquals(sk.get(), {1, 2, 4, 5, 7});
+
+  // Deleting is a no-op is the object is not found.
+  value = TEST_INT_new(100);
+  ASSERT_TRUE(value);
+  EXPECT_FALSE(sk_TEST_INT_delete_ptr(sk.get(), value.get()));
+
+  // Insert nullptr to test deep copy handling of it.
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), nullptr, 0));
+  ExpectStackEquals(sk.get(), {kNull, 1, 2, 4, 5, 7});
+
+  // Test both deep and shallow copies.
+  bssl::UniquePtr<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy(
+      sk.get(),
+      [](TEST_INT *x) -> TEST_INT * {
+        return x == nullptr ? nullptr : TEST_INT_new(*x).release();
+      },
+      TEST_INT_free));
+  ASSERT_TRUE(copy);
+  ExpectStackEquals(copy.get(), {kNull, 1, 2, 4, 5, 7});
+
+  ShallowStack shallow(sk_TEST_INT_dup(sk.get()));
+  ASSERT_TRUE(shallow);
+  ASSERT_EQ(sk_TEST_INT_num(sk.get()), sk_TEST_INT_num(shallow.get()));
+  for (size_t i = 0; i < sk_TEST_INT_num(sk.get()); i++) {
+    EXPECT_EQ(sk_TEST_INT_value(sk.get(), i),
+              sk_TEST_INT_value(shallow.get(), i));
+  }
+
+  // Deep copies may fail. This should clean up temporaries.
+  EXPECT_FALSE(sk_TEST_INT_deep_copy(sk.get(),
+                                     [](TEST_INT *x) -> TEST_INT * {
+                                       return x == nullptr || *x == 4
+                                                  ? nullptr
+                                                  : TEST_INT_new(*x).release();
+                                     },
+                                     TEST_INT_free));
+
+  // sk_TEST_INT_zero clears a stack, but does not free the elements.
+  ShallowStack shallow2(sk_TEST_INT_dup(sk.get()));
+  ASSERT_TRUE(shallow2);
+  sk_TEST_INT_zero(shallow2.get());
+  ExpectStackEquals(shallow2.get(), {});
+}
+
+TEST(StackTest, BigStack) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null());
+  ASSERT_TRUE(sk);
+
+  std::vector<int> expected;
+  static const int kCount = 100000;
+  for (int i = 0; i < kCount; i++) {
+    auto value = TEST_INT_new(i);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+    expected.push_back(i);
+  }
+  ExpectStackEquals(sk.get(), expected);
+}
+
+static uint64_t g_compare_count = 0;
+
+static int compare(const TEST_INT **a, const TEST_INT **b) {
+  g_compare_count++;
+  if (**a < **b) {
+    return -1;
+  }
+  if (**a > **b) {
+    return 1;
+  }
+  return 0;
+}
+
+static int compare_reverse(const TEST_INT **a, const TEST_INT **b) {
+  return -compare(a, b);
+}
+
+TEST(StackTest, Sorted) {
+  std::vector<int> vec_sorted = {0, 1, 2, 3, 4, 5, 6};
+  std::vector<int> vec = vec_sorted;
+  do {
+    bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+    ASSERT_TRUE(sk);
+    for (int v : vec) {
+      auto value = TEST_INT_new(v);
+      ASSERT_TRUE(value);
+      ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+    }
+
+    // The stack is not (known to be) sorted.
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+
+    // With a comparison function, find matches by value.
+    auto ten = TEST_INT_new(10);
+    ASSERT_TRUE(ten);
+    size_t index;
+    EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+
+    auto three = TEST_INT_new(3);
+    ASSERT_TRUE(three);
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3, *sk_TEST_INT_value(sk.get(), index));
+
+    sk_TEST_INT_sort(sk.get());
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+    ExpectStackEquals(sk.get(), vec_sorted);
+
+    // Sorting an already-sorted list is a no-op.
+    uint64_t old_compare_count = g_compare_count;
+    sk_TEST_INT_sort(sk.get());
+    EXPECT_EQ(old_compare_count, g_compare_count);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+    ExpectStackEquals(sk.get(), vec_sorted);
+
+    // When sorted, find uses binary search.
+    ASSERT_TRUE(ten);
+    EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+
+    ASSERT_TRUE(three);
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Copies preserve comparison and sorted information.
+    bssl::UniquePtr<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy(
+        sk.get(),
+        [](TEST_INT *x) -> TEST_INT * { return TEST_INT_new(*x).release(); },
+        TEST_INT_free));
+    ASSERT_TRUE(copy);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(copy.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(copy.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    ShallowStack copy2(sk_TEST_INT_dup(sk.get()));
+    ASSERT_TRUE(copy2);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(copy2.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(copy2.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Removing elements does not affect sortedness.
+    TEST_INT_free(sk_TEST_INT_delete(sk.get(), 0));
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+
+    // Changing the comparison function invalidates sortedness.
+    sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse);
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(2u, index);
+
+    sk_TEST_INT_sort(sk.get());
+    ExpectStackEquals(sk.get(), {6, 5, 4, 3, 2, 1});
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Inserting a new element invalidates sortedness.
+    auto tmp = TEST_INT_new(10);
+    ASSERT_TRUE(tmp);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(tmp)));
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+    EXPECT_EQ(6u, index);
+  } while (std::next_permutation(vec.begin(), vec.end()));
+}
+
+// sk_*_find should return the first matching element in all cases.
+TEST(StackTest, FindFirst) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+  auto value = TEST_INT_new(1);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  for (int i = 0; i < 10; i++) {
+    value = TEST_INT_new(2);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  }
+
+  const TEST_INT *two = sk_TEST_INT_value(sk.get(), 1);
+  // Pointer-based equality.
+  size_t index;
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, unsorted.
+  sk_TEST_INT_set_cmp_func(sk.get(), compare);
+  EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, sorted.
+  sk_TEST_INT_sort(sk.get());
+  EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, sorted and at the front.
+  sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse);
+  sk_TEST_INT_sort(sk.get());
+  EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(0u, index);
+}
+
+// Exhaustively test the binary search.
+TEST(StackTest, BinarySearch) {
+  static const size_t kCount = 100;
+  for (size_t i = 0; i < kCount; i++) {
+    SCOPED_TRACE(i);
+    for (size_t j = i; j <= kCount; j++) {
+      SCOPED_TRACE(j);
+      // Make a stack where [0, i) are below, [i, j) match, and [j, kCount) are
+      // above.
+      bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+      ASSERT_TRUE(sk);
+      for (size_t k = 0; k < i; k++) {
+        auto value = TEST_INT_new(-1);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      for (size_t k = i; k < j; k++) {
+        auto value = TEST_INT_new(0);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      for (size_t k = j; k < kCount; k++) {
+        auto value = TEST_INT_new(1);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      sk_TEST_INT_sort(sk.get());
+
+      auto key = TEST_INT_new(0);
+      ASSERT_TRUE(key);
+
+      size_t idx;
+      int found = sk_TEST_INT_find(sk.get(), &idx, key.get());
+      if (i == j) {
+        EXPECT_FALSE(found);
+      } else {
+        ASSERT_TRUE(found);
+        EXPECT_EQ(i, idx);
+      }
+    }
+  }
+}
diff --git a/src/crypto/test/CMakeLists.txt b/src/crypto/test/CMakeLists.txt
index 90707dd..3e02c3c 100644
--- a/src/crypto/test/CMakeLists.txt
+++ b/src/crypto/test/CMakeLists.txt
@@ -9,6 +9,8 @@
   wycheproof_util.cc
 )
 
+add_dependencies(test_support global_target)
+
 add_library(
   boringssl_gtest_main
 
@@ -16,3 +18,5 @@
 
   gtest_main.cc
 )
+
+add_dependencies(boringssl_gtest_main global_target)
diff --git a/src/crypto/test/gtest_main.cc b/src/crypto/test/gtest_main.cc
index 4071040..5dc8b23 100644
--- a/src/crypto/test/gtest_main.cc
+++ b/src/crypto/test/gtest_main.cc
@@ -12,13 +12,26 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#include <string.h>
+
 #include <gtest/gtest.h>
 
+#include <openssl/rand.h>
+
 #include "gtest_main.h"
 
 
 int main(int argc, char **argv) {
   testing::InitGoogleTest(&argc, argv);
   bssl::SetupGoogleTest();
+
+#if !defined(OPENSSL_WINDOWS)
+  for (int i = 1; i < argc; i++) {
+    if (strcmp(argv[i], "--fork_unsafe_buffering") == 0) {
+      RAND_enable_fork_unsafe_buffering(-1);
+    }
+  }
+#endif
+
   return RUN_ALL_TESTS();
 }
diff --git a/src/crypto/test/gtest_main.h b/src/crypto/test/gtest_main.h
index d21af10..20ccf21 100644
--- a/src/crypto/test/gtest_main.h
+++ b/src/crypto/test/gtest_main.h
@@ -27,10 +27,12 @@
 OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
 OPENSSL_MSVC_PRAGMA(warning(pop))
+#else
+#include <signal.h>
 #endif
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 class ErrorTestEventListener : public testing::EmptyTestEventListener {
  public:
@@ -67,13 +69,17 @@
     fprintf(stderr, "Didn't get expected version: %x\n", wsa_data.wVersion);
     exit(1);
   }
+#else
+  // Some tests create pipes. We check return values, so avoid being killed by
+  // |SIGPIPE|.
+  signal(SIGPIPE, SIG_IGN);
 #endif
 
   testing::UnitTest::GetInstance()->listeners().Append(
       new ErrorTestEventListener);
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 
 #endif  // OPENSSL_HEADER_CRYPTO_TEST_GTEST_MAIN_H
diff --git a/src/crypto/thread_none.c b/src/crypto/thread_none.c
index 718d960..4f07b9d 100644
--- a/src/crypto/thread_none.c
+++ b/src/crypto/thread_none.c
@@ -14,7 +14,7 @@
 
 #include "internal.h"
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 
 void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock) {}
 
@@ -56,4 +56,4 @@
   return 1;
 }
 
-#endif  // OPENSSL_NO_THREADS
+#endif  // !OPENSSL_THREADS
diff --git a/src/crypto/thread_test.cc b/src/crypto/thread_test.cc
index f89b22e..f9fad9b 100644
--- a/src/crypto/thread_test.cc
+++ b/src/crypto/thread_test.cc
@@ -25,7 +25,7 @@
 #include "test/test_util.h"
 
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 
 static unsigned g_once_init_called = 0;
 
@@ -130,4 +130,4 @@
   thread.join();
 }
 
-#endif  // !OPENSSL_NO_THREADS
+#endif  // OPENSSL_THREADS
diff --git a/src/crypto/x509/CMakeLists.txt b/src/crypto/x509/CMakeLists.txt
deleted file mode 100644
index 74001e7..0000000
--- a/src/crypto/x509/CMakeLists.txt
+++ /dev/null
@@ -1,57 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  x509
-
-  OBJECT
-
-  a_digest.c
-  a_sign.c
-  a_strex.c
-  a_verify.c
-  algorithm.c
-  asn1_gen.c
-  by_dir.c
-  by_file.c
-  i2d_pr.c
-  rsa_pss.c
-  t_crl.c
-  t_req.c
-  t_x509.c
-  t_x509a.c
-  x509.c
-  x509_att.c
-  x509_cmp.c
-  x509_d2.c
-  x509_def.c
-  x509_ext.c
-  x509_lu.c
-  x509_obj.c
-  x509_r2x.c
-  x509_req.c
-  x509_set.c
-  x509_trs.c
-  x509_txt.c
-  x509_v3.c
-  x509_vfy.c
-  x509_vpm.c
-  x509cset.c
-  x509name.c
-  x509rset.c
-  x509spki.c
-  x_algor.c
-  x_all.c
-  x_attrib.c
-  x_crl.c
-  x_exten.c
-  x_info.c
-  x_name.c
-  x_pkey.c
-  x_pubkey.c
-  x_req.c
-  x_sig.c
-  x_spki.c
-  x_val.c
-  x_x509.c
-  x_x509a.c
-)
diff --git a/src/crypto/x509/by_file.c b/src/crypto/x509/by_file.c
index 555cb85..dfff425 100644
--- a/src/crypto/x509/by_file.c
+++ b/src/crypto/x509/by_file.c
@@ -138,14 +138,15 @@
         for (;;) {
             x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
             if (x == NULL) {
-                if ((ERR_GET_REASON(ERR_peek_last_error()) ==
-                     PEM_R_NO_START_LINE) && (count > 0)) {
+                uint32_t error = ERR_peek_last_error();
+                if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                    ERR_GET_REASON(error) == PEM_R_NO_START_LINE &&
+                    count > 0) {
                     ERR_clear_error();
                     break;
-                } else {
-                    OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
-                    goto err;
                 }
+                OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
+                goto err;
             }
             i = X509_STORE_add_cert(ctx->store_ctx, x);
             if (!i)
@@ -197,14 +198,15 @@
         for (;;) {
             x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
             if (x == NULL) {
-                if ((ERR_GET_REASON(ERR_peek_last_error()) ==
-                     PEM_R_NO_START_LINE) && (count > 0)) {
+                uint32_t error = ERR_peek_last_error();
+                if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                    ERR_GET_REASON(error) == PEM_R_NO_START_LINE &&
+                    count > 0) {
                     ERR_clear_error();
                     break;
-                } else {
-                    OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
-                    goto err;
                 }
+                OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
+                goto err;
             }
             i = X509_STORE_add_crl(ctx->store_ctx, x);
             if (!i)
diff --git a/src/crypto/x509/t_crl.c b/src/crypto/x509/t_crl.c
index 6c347cb..dc9b87f 100644
--- a/src/crypto/x509/t_crl.c
+++ b/src/crypto/x509/t_crl.c
@@ -64,17 +64,14 @@
 #ifndef OPENSSL_NO_FP_API
 int X509_CRL_print_fp(FILE *fp, X509_CRL *x)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = X509_CRL_print(b, x);
+    int ret = X509_CRL_print(b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
diff --git a/src/crypto/x509/t_req.c b/src/crypto/x509/t_req.c
index 39c836c..2fd36f8 100644
--- a/src/crypto/x509/t_req.c
+++ b/src/crypto/x509/t_req.c
@@ -65,13 +65,11 @@
 
 
 int X509_REQ_print_fp(FILE *fp, X509_REQ *x) {
-  BIO *bio = BIO_new(BIO_s_file());
+  BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);
   if (bio == NULL) {
     OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
     return 0;
   }
-
-  BIO_set_fp(bio, fp, BIO_NOCLOSE);
   int ret = X509_REQ_print(bio, x);
   BIO_free(bio);
   return ret;
diff --git a/src/crypto/x509/t_x509.c b/src/crypto/x509/t_x509.c
index 3339523..e45a765 100644
--- a/src/crypto/x509/t_x509.c
+++ b/src/crypto/x509/t_x509.c
@@ -72,17 +72,14 @@
 int X509_print_ex_fp(FILE *fp, X509 *x, unsigned long nmflag,
                      unsigned long cflag)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = X509_print_ex(b, x, nmflag, cflag);
+    int ret = X509_print_ex(b, x, nmflag, cflag);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int X509_print_fp(FILE *fp, X509 *x)
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index 551bd8c..bf0b29a 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -627,6 +627,38 @@
     "XyPwWPbymSTpzKhB4xB7qQ==\n"
     "-----END CERTIFICATE-----\n";
 
+static const char kSelfSignedMismatchAlgorithms[] =
+    "-----BEGIN CERTIFICATE-----\n"
+    "MIIFMjCCAxqgAwIBAgIJAL0mG5fOeJ7xMA0GCSqGSIb3DQEBDQUAMC0xCzAJBgNV\n"
+    "BAYTAkdCMQ8wDQYDVQQHDAZMb25kb24xDTALBgNVBAoMBFRlc3QwIBcNMTgwOTE3\n"
+    "MTIxNzU3WhgPMjExODA4MjQxMjE3NTdaMC0xCzAJBgNVBAYTAkdCMQ8wDQYDVQQH\n"
+    "DAZMb25kb24xDTALBgNVBAoMBFRlc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw\n"
+    "ggIKAoICAQDCMhBrRAGGw+n2GdctBr/cEK4FZA6ajiHjihgpCHoSBdyL4R2jGKLS\n"
+    "g0WgaMXa1HpkKN7LcIySosEBPlmcRkr1RqbEvQStOSvoFCXYvtx3alM6HTbXMcDR\n"
+    "mqoKoABP6LXsPSoMWIgqMtP2X9EOppzHVIK1yFYFfbIlvYUV2Ka+MuMe0Vh5wvD1\n"
+    "4GanPb+cWSKgdRSVQovCCMY3yWtZKVEaxRpCsk/mYYIFWz0tcgMjIKwDx1XXgiAV\n"
+    "nU6NK43xbaw3XhtnaD/pv9lhTTbNrlcln9LjTD097BaK4R+1AEPHnpfxA9Ui3upn\n"
+    "kbsNUdGdOB0ksZi/vd7lh833YgquQUIAhYrbfvq/HFCpVV1gljzlS3sqULYpLE//\n"
+    "i3OsuL2mE+CYIJGpIi2GeJJWXciNMTJDOqTn+fRDtVb4RPp4Y70DJirp7XzaBi3q\n"
+    "H0edANCzPSRCDbZsOhzIXhXshldiXVRX666DDlbMQgLTEnNKrkwv6DmU8o15XQsb\n"
+    "8k1Os2YwXmkEOxUQ7AJZXVTZSf6UK9Znmdq1ZrHjybMfRUkHVxJcnKvrxfryralv\n"
+    "gzfvu+D6HuxrCo3Ojqa+nDgIbxKEBtdrcsMhq1jWPFhjwo1fSadAkKOfdCAuXJRD\n"
+    "THg3b4Sf+W7Cpc570YHrIpBf7WFl2XsPcEM0mJZ5+yATASCubNozQwIDAQABo1Mw\n"
+    "UTAdBgNVHQ4EFgQUES0hupZSqY21JOba10QyZuxm91EwHwYDVR0jBBgwFoAUES0h\n"
+    "upZSqY21JOba10QyZuxm91EwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsF\n"
+    "AAOCAgEABTN5S30ng/RMpBweDm2N561PdpaCdiRXtAFCRVWR2mkDYC/Xj9Vqe6be\n"
+    "PyM7L/5OKYVjzF1yJu67z/dx+ja5o+41g17jdqla7hyPx+9B4uRyDh+1KJTa+duj\n"
+    "mw/aA1LCr6O6W4WizDOsChJ6FaB2Y1+GlFnKWb5nUdhVJqXQE1WOX9dZnw8Y4Npd\n"
+    "VmAsjWot0BZorJrt3fwfcv3QfA896twkbo7Llv/8qzg4sXZXZ4ZtgAOqnPngiSn+\n"
+    "JT/vYCXZ406VvAFpFqMcVz2dO/VGuL8lGIMHRKNyafrsV81EzH1W/XmRWOgvgj6r\n"
+    "yQI63ln/AMY72HQ97xLkE1xKunGz6bK5Ug5+O43Uftc4Mb6MUgzo+ZqEQ3Ob+cAV\n"
+    "cvjmtwDaPO/O39O5Xq0tLTlkn2/cKf4OQ6S++GDxzyRVHh5JXgP4j9+jfZY57Woy\n"
+    "R1bE7N50JjY4cDermBJKdlBIjL7UPhqmLyaG7V0hBitFlgGBUCcJtJOV0xYd5aF3\n"
+    "pxNkvMXhBmh95fjxJ0cJjpO7tN1RAwtMMNgsl7OUbuVRQCHOPW5DgP5qY21jDeRn\n"
+    "BY82382l+9QzykmJLI5MZnmj4BA9uIDCwMtoTTvP++SsvhUAbuvh7MOOUQL0EY4m\n"
+    "KStYq7X9PKseN+PvmfeoffIKc5R/Ha39oi7cGMVHCr8aiEhsf94=\n"
+    "-----END CERTIFICATE-----\n";
+
 // CertFromPEM parses the given, NUL-terminated pem block and returns an
 // |X509*|.
 static bssl::UniquePtr<X509> CertFromPEM(const char *pem) {
@@ -1504,3 +1536,138 @@
   EXPECT_EQ(X509_V_ERR_INVALID_CA,
             Verify(leaf.get(), {root.get()}, {intermediate.get()}, {}, 0));
 }
+
+TEST(X509Test, MismatchAlgorithms) {
+  bssl::UniquePtr<X509> cert(CertFromPEM(kSelfSignedMismatchAlgorithms));
+  ASSERT_TRUE(cert);
+
+  bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(cert.get()));
+  ASSERT_TRUE(pkey);
+
+  EXPECT_FALSE(X509_verify(cert.get(), pkey.get()));
+  uint32_t err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_X509, ERR_GET_LIB(err));
+  EXPECT_EQ(X509_R_SIGNATURE_ALGORITHM_MISMATCH, ERR_GET_REASON(err));
+}
+
+TEST(X509Test, PEMX509Info) {
+  std::string cert = kRootCAPEM;
+  auto cert_obj = CertFromPEM(kRootCAPEM);
+  ASSERT_TRUE(cert_obj);
+
+  std::string rsa = kRSAKey;
+  auto rsa_obj = PrivateKeyFromPEM(kRSAKey);
+  ASSERT_TRUE(rsa_obj);
+
+  std::string crl = kBasicCRL;
+  auto crl_obj = CRLFromPEM(kBasicCRL);
+  ASSERT_TRUE(crl_obj);
+
+  std::string unknown =
+      "-----BEGIN UNKNOWN-----\n"
+      "AAAA\n"
+      "-----END UNKNOWN-----\n";
+
+  std::string invalid =
+      "-----BEGIN CERTIFICATE-----\n"
+      "AAAA\n"
+      "-----END CERTIFICATE-----\n";
+
+  // Each X509_INFO contains at most one certificate, CRL, etc. The format
+  // creates a new X509_INFO when a repeated type is seen.
+  std::string pem =
+      // The first few entries have one of everything in different orders.
+      cert + rsa + crl +
+      rsa + crl + cert +
+      // Unknown types are ignored.
+      crl + unknown + cert + rsa +
+      // Seeing a new certificate starts a new entry, so now we have a bunch of
+      // certificate-only entries.
+      cert + cert + cert +
+      // The key folds into the certificate's entry.
+      cert + rsa +
+      // Doubled keys also start new entries.
+      rsa + rsa + rsa + rsa + crl +
+      // As do CRLs.
+      crl + crl;
+
+  const struct ExpectedInfo {
+    const X509 *cert;
+    const EVP_PKEY *key;
+    const X509_CRL *crl;
+  } kExpected[] = {
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), crl_obj.get()},
+    {nullptr, nullptr, crl_obj.get()},
+    {nullptr, nullptr, crl_obj.get()},
+  };
+
+  auto check_info = [](const ExpectedInfo *expected, const X509_INFO *info) {
+    if (expected->cert != nullptr) {
+      EXPECT_EQ(0, X509_cmp(expected->cert, info->x509));
+    } else {
+      EXPECT_EQ(nullptr, info->x509);
+    }
+    if (expected->crl != nullptr) {
+      EXPECT_EQ(0, X509_CRL_cmp(expected->crl, info->crl));
+    } else {
+      EXPECT_EQ(nullptr, info->crl);
+    }
+    if (expected->key != nullptr) {
+      ASSERT_NE(nullptr, info->x_pkey);
+      // EVP_PKEY_cmp returns one if the keys are equal.
+      EXPECT_EQ(1, EVP_PKEY_cmp(expected->key, info->x_pkey->dec_pkey));
+    } else {
+      EXPECT_EQ(nullptr, info->x_pkey);
+    }
+  };
+
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem.data(), pem.size()));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<STACK_OF(X509_INFO)> infos(
+      PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr));
+  ASSERT_TRUE(infos);
+  ASSERT_EQ(OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) {
+    SCOPED_TRACE(i);
+    check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i));
+  }
+
+  // Passing an existing stack appends to it.
+  bio.reset(BIO_new_mem_buf(pem.data(), pem.size()));
+  ASSERT_TRUE(bio);
+  ASSERT_EQ(infos.get(),
+            PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr));
+  ASSERT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) {
+    SCOPED_TRACE(i);
+    check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i));
+    check_info(
+        &kExpected[i],
+        sk_X509_INFO_value(infos.get(), i + OPENSSL_ARRAY_SIZE(kExpected)));
+  }
+
+  // Gracefully handle errors in both the append and fresh cases.
+  std::string bad_pem = cert + cert + invalid;
+
+  bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size()));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<STACK_OF(X509_INFO)> infos2(
+      PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr));
+  EXPECT_FALSE(infos2);
+
+  bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size()));
+  ASSERT_TRUE(bio);
+  EXPECT_FALSE(
+      PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr));
+  EXPECT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+}
diff --git a/src/crypto/x509/x_all.c b/src/crypto/x509/x_all.c
index 2a93b87..a37d7bd 100644
--- a/src/crypto/x509/x_all.c
+++ b/src/crypto/x509/x_all.c
@@ -54,19 +54,25 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
+#include <openssl/x509.h>
+
+#include <limits.h>
+
 #include <openssl/asn1.h>
 #include <openssl/buf.h>
 #include <openssl/digest.h>
 #include <openssl/dsa.h>
 #include <openssl/evp.h>
+#include <openssl/mem.h>
 #include <openssl/rsa.h>
 #include <openssl/stack.h>
-#include <openssl/x509.h>
 
 int X509_verify(X509 *a, EVP_PKEY *r)
 {
-    if (X509_ALGOR_cmp(a->sig_alg, a->cert_info->signature))
+    if (X509_ALGOR_cmp(a->sig_alg, a->cert_info->signature)) {
+        OPENSSL_PUT_ERROR(X509, X509_R_SIGNATURE_ALGORITHM_MISMATCH);
         return 0;
+    }
     return (ASN1_item_verify(ASN1_ITEM_rptr(X509_CINF), a->sig_alg,
                              a->signature, a->cert_info, r));
 }
@@ -199,154 +205,102 @@
 }
 
 #ifndef OPENSSL_NO_FP_API
-RSA *d2i_RSAPrivateKey_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp_of(RSA, RSA_new, d2i_RSAPrivateKey, fp, rsa);
-}
 
-int i2d_RSAPrivateKey_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp_of_const(RSA, i2d_RSAPrivateKey, fp, rsa);
-}
+#define IMPLEMENT_D2I_FP(type, name, bio_func) \
+  type *name(FILE *fp, type **obj) {           \
+    BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);    \
+    if (bio == NULL) {                         \
+      return NULL;                             \
+    }                                          \
+    type *ret = bio_func(bio, obj);            \
+    BIO_free(bio);                             \
+    return ret;                                \
+  }
 
-RSA *d2i_RSAPublicKey_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp_of(RSA, RSA_new, d2i_RSAPublicKey, fp, rsa);
-}
+#define IMPLEMENT_I2D_FP(type, name, bio_func) \
+  int name(FILE *fp, type *obj) {              \
+    BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);    \
+    if (bio == NULL) {                         \
+      return 0;                                \
+    }                                          \
+    int ret = bio_func(bio, obj);              \
+    BIO_free(bio);                             \
+    return ret;                                \
+  }
 
-RSA *d2i_RSA_PUBKEY_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp((void *(*)(void))
-                       RSA_new, (D2I_OF(void)) d2i_RSA_PUBKEY, fp,
-                       (void **)rsa);
-}
+IMPLEMENT_D2I_FP(RSA, d2i_RSAPrivateKey_fp, d2i_RSAPrivateKey_bio)
+IMPLEMENT_I2D_FP(RSA, i2d_RSAPrivateKey_fp, i2d_RSAPrivateKey_bio)
 
-int i2d_RSAPublicKey_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp_of_const(RSA, i2d_RSAPublicKey, fp, rsa);
-}
+IMPLEMENT_D2I_FP(RSA, d2i_RSAPublicKey_fp, d2i_RSAPublicKey_bio)
+IMPLEMENT_I2D_FP(RSA, i2d_RSAPublicKey_fp, i2d_RSAPublicKey_bio)
 
-int i2d_RSA_PUBKEY_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp((I2D_OF_const(void))i2d_RSA_PUBKEY, fp, rsa);
-}
+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
 
-RSA *d2i_RSAPrivateKey_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSAPrivateKey, bp, rsa);
-}
+#define IMPLEMENT_D2I_BIO(type, name, d2i_func)         \
+  type *name(BIO *bio, type **obj) {                    \
+    uint8_t *data;                                      \
+    size_t len;                                         \
+    if (!BIO_read_asn1(bio, &data, &len, 100 * 1024)) { \
+      return NULL;                                      \
+    }                                                   \
+    const uint8_t *ptr = data;                          \
+    type *ret = d2i_func(obj, &ptr, (long)len);         \
+    OPENSSL_free(data);                                 \
+    return ret;                                         \
+  }
 
-int i2d_RSAPrivateKey_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSAPrivateKey, bp, rsa);
-}
+#define IMPLEMENT_I2D_BIO(type, name, i2d_func) \
+  int name(BIO *bio, type *obj) {               \
+    uint8_t *data = NULL;                       \
+    int len = i2d_func(obj, &data);             \
+    if (len < 0) {                              \
+      return 0;                                 \
+    }                                           \
+    int ret = BIO_write_all(bio, data, len);    \
+    OPENSSL_free(data);                         \
+    return ret;                                 \
+  }
 
-RSA *d2i_RSAPublicKey_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSAPublicKey, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSAPrivateKey_bio, d2i_RSAPrivateKey)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSAPrivateKey_bio, i2d_RSAPrivateKey)
 
-RSA *d2i_RSA_PUBKEY_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSA_PUBKEY, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSAPublicKey_bio, d2i_RSAPublicKey)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSAPublicKey_bio, i2d_RSAPublicKey)
 
-int i2d_RSAPublicKey_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSAPublicKey, bp, rsa);
-}
-
-int i2d_RSA_PUBKEY_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSA_PUBKEY, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSA_PUBKEY_bio, d2i_RSA_PUBKEY)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSA_PUBKEY_bio, i2d_RSA_PUBKEY)
 
 #ifndef OPENSSL_NO_DSA
 # ifndef OPENSSL_NO_FP_API
-DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa)
-{
-    return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSAPrivateKey, fp, dsa);
-}
+IMPLEMENT_D2I_FP(DSA, d2i_DSAPrivateKey_fp, d2i_DSAPrivateKey_bio)
+IMPLEMENT_I2D_FP(DSA, i2d_DSAPrivateKey_fp, i2d_DSAPrivateKey_bio)
 
-int i2d_DSAPrivateKey_fp(FILE *fp, DSA *dsa)
-{
-    return ASN1_i2d_fp_of_const(DSA, i2d_DSAPrivateKey, fp, dsa);
-}
-
-DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa)
-{
-    return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSA_PUBKEY, fp, dsa);
-}
-
-int i2d_DSA_PUBKEY_fp(FILE *fp, DSA *dsa)
-{
-    return ASN1_i2d_fp_of_const(DSA, i2d_DSA_PUBKEY, fp, dsa);
-}
+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
 
-DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa)
-{
-    return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSAPrivateKey, bp, dsa);
-}
+IMPLEMENT_D2I_BIO(DSA, d2i_DSAPrivateKey_bio, d2i_DSAPrivateKey)
+IMPLEMENT_I2D_BIO(DSA, i2d_DSAPrivateKey_bio, i2d_DSAPrivateKey)
 
-int i2d_DSAPrivateKey_bio(BIO *bp, DSA *dsa)
-{
-    return ASN1_i2d_bio_of_const(DSA, i2d_DSAPrivateKey, bp, dsa);
-}
-
-DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa)
-{
-    return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSA_PUBKEY, bp, dsa);
-}
-
-int i2d_DSA_PUBKEY_bio(BIO *bp, DSA *dsa)
-{
-    return ASN1_i2d_bio_of_const(DSA, i2d_DSA_PUBKEY, bp, dsa);
-}
-
+IMPLEMENT_D2I_BIO(DSA, d2i_DSA_PUBKEY_bio, d2i_DSA_PUBKEY)
+IMPLEMENT_I2D_BIO(DSA, i2d_DSA_PUBKEY_bio, i2d_DSA_PUBKEY)
 #endif
 
 #ifndef OPENSSL_NO_FP_API
-EC_KEY *d2i_EC_PUBKEY_fp(FILE *fp, EC_KEY **eckey)
-{
-    return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, fp, eckey);
-}
+IMPLEMENT_D2I_FP(EC_KEY, d2i_ECPrivateKey_fp, d2i_ECPrivateKey_bio)
+IMPLEMENT_I2D_FP(EC_KEY, i2d_ECPrivateKey_fp, i2d_ECPrivateKey_bio)
 
-int i2d_EC_PUBKEY_fp(FILE *fp, EC_KEY *eckey)
-{
-    return ASN1_i2d_fp_of_const(EC_KEY, i2d_EC_PUBKEY, fp, eckey);
-}
-
-EC_KEY *d2i_ECPrivateKey_fp(FILE *fp, EC_KEY **eckey)
-{
-    return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, fp, eckey);
-}
-
-int i2d_ECPrivateKey_fp(FILE *fp, EC_KEY *eckey)
-{
-    return ASN1_i2d_fp_of_const(EC_KEY, i2d_ECPrivateKey, fp, eckey);
-}
+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
-EC_KEY *d2i_EC_PUBKEY_bio(BIO *bp, EC_KEY **eckey)
-{
-    return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, bp, eckey);
-}
 
-int i2d_EC_PUBKEY_bio(BIO *bp, EC_KEY *ecdsa)
-{
-    return ASN1_i2d_bio_of_const(EC_KEY, i2d_EC_PUBKEY, bp, ecdsa);
-}
+IMPLEMENT_D2I_BIO(EC_KEY, d2i_ECPrivateKey_bio, d2i_ECPrivateKey)
+IMPLEMENT_I2D_BIO(EC_KEY, i2d_ECPrivateKey_bio, i2d_ECPrivateKey)
 
-EC_KEY *d2i_ECPrivateKey_bio(BIO *bp, EC_KEY **eckey)
-{
-    return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, bp, eckey);
-}
-
-int i2d_ECPrivateKey_bio(BIO *bp, EC_KEY *eckey)
-{
-    return ASN1_i2d_bio_of_const(EC_KEY, i2d_ECPrivateKey, bp, eckey);
-}
+IMPLEMENT_D2I_BIO(EC_KEY, d2i_EC_PUBKEY_bio, d2i_EC_PUBKEY)
+IMPLEMENT_I2D_BIO(EC_KEY, i2d_EC_PUBKEY_bio, i2d_EC_PUBKEY)
 
 int X509_pubkey_digest(const X509 *data, const EVP_MD *type,
                        unsigned char *md, unsigned int *len)
@@ -387,40 +341,18 @@
 }
 
 #ifndef OPENSSL_NO_FP_API
-X509_SIG *d2i_PKCS8_fp(FILE *fp, X509_SIG **p8)
-{
-    return ASN1_d2i_fp_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, fp, p8);
-}
-
-int i2d_PKCS8_fp(FILE *fp, X509_SIG *p8)
-{
-    return ASN1_i2d_fp_of(X509_SIG, i2d_X509_SIG, fp, p8);
-}
+IMPLEMENT_D2I_FP(X509_SIG, d2i_PKCS8_fp, d2i_PKCS8_bio)
+IMPLEMENT_I2D_FP(X509_SIG, i2d_PKCS8_fp, i2d_PKCS8_bio)
 #endif
 
-X509_SIG *d2i_PKCS8_bio(BIO *bp, X509_SIG **p8)
-{
-    return ASN1_d2i_bio_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, bp, p8);
-}
-
-int i2d_PKCS8_bio(BIO *bp, X509_SIG *p8)
-{
-    return ASN1_i2d_bio_of(X509_SIG, i2d_X509_SIG, bp, p8);
-}
+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
-PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,
-                                                PKCS8_PRIV_KEY_INFO **p8inf)
-{
-    return ASN1_d2i_fp_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new,
-                          d2i_PKCS8_PRIV_KEY_INFO, fp, p8inf);
-}
-
-int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, PKCS8_PRIV_KEY_INFO *p8inf)
-{
-    return ASN1_i2d_fp_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, fp,
-                          p8inf);
-}
+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,
+                 i2d_PKCS8_PRIV_KEY_INFO_bio)
 
 int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, EVP_PKEY *key)
 {
@@ -434,38 +366,16 @@
     return ret;
 }
 
-int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_fp_of_const(EVP_PKEY, i2d_PrivateKey, fp, pkey);
-}
+IMPLEMENT_D2I_FP(EVP_PKEY, d2i_PrivateKey_fp, d2i_PrivateKey_bio)
+IMPLEMENT_I2D_FP(EVP_PKEY, i2d_PrivateKey_fp, i2d_PrivateKey_bio)
 
-EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a)
-{
-    return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, fp, a);
-}
+IMPLEMENT_D2I_FP(EVP_PKEY, d2i_PUBKEY_fp, d2i_PUBKEY_bio)
+IMPLEMENT_I2D_FP(EVP_PKEY, i2d_PUBKEY_fp, i2d_PUBKEY_bio)
 
-int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_fp_of_const(EVP_PKEY, i2d_PUBKEY, fp, pkey);
-}
-
-EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a)
-{
-    return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, fp, a);
-}
-
-PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp,
-                                                 PKCS8_PRIV_KEY_INFO **p8inf)
-{
-    return ASN1_d2i_bio_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new,
-                           d2i_PKCS8_PRIV_KEY_INFO, bp, p8inf);
-}
-
-int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, PKCS8_PRIV_KEY_INFO *p8inf)
-{
-    return ASN1_i2d_bio_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, bp,
-                           p8inf);
-}
+IMPLEMENT_D2I_BIO(PKCS8_PRIV_KEY_INFO, d2i_PKCS8_PRIV_KEY_INFO_bio,
+                  d2i_PKCS8_PRIV_KEY_INFO)
+IMPLEMENT_I2D_BIO(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO_bio,
+                  i2d_PKCS8_PRIV_KEY_INFO)
 
 int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key)
 {
@@ -480,32 +390,11 @@
 }
 #endif
 
-int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_bio_of_const(EVP_PKEY, i2d_PrivateKey, bp, pkey);
-}
+IMPLEMENT_D2I_BIO(EVP_PKEY, d2i_PrivateKey_bio, d2i_AutoPrivateKey)
+IMPLEMENT_I2D_BIO(EVP_PKEY, i2d_PrivateKey_bio, i2d_PrivateKey)
 
-EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a)
-{
-    return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, bp, a);
-}
+IMPLEMENT_D2I_BIO(EVP_PKEY, d2i_PUBKEY_bio, d2i_PUBKEY)
+IMPLEMENT_I2D_BIO(EVP_PKEY, i2d_PUBKEY_bio, i2d_PUBKEY)
 
-int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_bio_of_const(EVP_PKEY, i2d_PUBKEY, bp, pkey);
-}
-
-EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a)
-{
-    return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, bp, a);
-}
-
-DH *d2i_DHparams_bio(BIO *bp, DH **dh)
-{
-    return ASN1_d2i_bio_of(DH, DH_new, d2i_DHparams, bp, dh);
-}
-
-int i2d_DHparams_bio(BIO *bp, const DH *dh)
-{
-    return ASN1_i2d_bio_of_const(DH, i2d_DHparams, bp, dh);
-}
+IMPLEMENT_D2I_BIO(DH, d2i_DHparams_bio, d2i_DHparams)
+IMPLEMENT_I2D_BIO(const DH, i2d_DHparams_bio, i2d_DHparams)
diff --git a/src/crypto/x509v3/CMakeLists.txt b/src/crypto/x509v3/CMakeLists.txt
deleted file mode 100644
index 6119639..0000000
--- a/src/crypto/x509v3/CMakeLists.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  x509v3
-
-  OBJECT
-
-  # v3_addr.c - disabled by upstream by default.
-  # v3_asid.c - disabled by upstream by default.
-  # v3_ocsp.c - missing OCSP for now.
-
-  pcy_cache.c
-  pcy_data.c
-  pcy_lib.c
-  pcy_map.c
-  pcy_node.c
-  pcy_tree.c
-  v3_akey.c
-  v3_akeya.c
-  v3_alt.c
-  v3_bcons.c
-  v3_bitst.c
-  v3_conf.c
-  v3_cpols.c
-  v3_crld.c
-  v3_enum.c
-  v3_extku.c
-  v3_genn.c
-  v3_ia5.c
-  v3_info.c
-  v3_int.c
-  v3_lib.c
-  v3_ncons.c
-  v3_ocsp.c
-  v3_pci.c
-  v3_pcia.c
-  v3_pcons.c
-  v3_pku.c
-  v3_pmaps.c
-  v3_prn.c
-  v3_purp.c
-  v3_skey.c
-  v3_sxnet.c
-  v3_utl.c
-)
diff --git a/src/crypto/x509v3/v3_genn.c b/src/crypto/x509v3/v3_genn.c
index 8c92687..552a524 100644
--- a/src/crypto/x509v3/v3_genn.c
+++ b/src/crypto/x509v3/v3_genn.c
@@ -100,12 +100,7 @@
 
 IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAMES)
 
-GENERAL_NAME *GENERAL_NAME_dup(GENERAL_NAME *a)
-{
-    return (GENERAL_NAME *)ASN1_dup((i2d_of_void *)i2d_GENERAL_NAME,
-                                    (d2i_of_void *)d2i_GENERAL_NAME,
-                                    (char *)a);
-}
+IMPLEMENT_ASN1_DUP_FUNCTION(GENERAL_NAME)
 
 /* Returns 0 if they are equal, != 0 otherwise. */
 int GENERAL_NAME_cmp(GENERAL_NAME *a, GENERAL_NAME *b)
diff --git a/src/decrepit/CMakeLists.txt b/src/decrepit/CMakeLists.txt
index bebc624..1cb5e11 100644
--- a/src/decrepit/CMakeLists.txt
+++ b/src/decrepit/CMakeLists.txt
@@ -1,41 +1,29 @@
 include_directories(../include)
 
-add_subdirectory(bio)
-add_subdirectory(blowfish)
-add_subdirectory(cast)
-add_subdirectory(cfb)
-add_subdirectory(des)
-add_subdirectory(dh)
-add_subdirectory(dsa)
-add_subdirectory(evp)
-add_subdirectory(obj)
-add_subdirectory(rc4)
-add_subdirectory(ripemd)
-add_subdirectory(rsa)
-add_subdirectory(ssl)
-add_subdirectory(x509)
-add_subdirectory(xts)
-
 add_library(
   decrepit
 
-  $<TARGET_OBJECTS:bio_decrepit>
-  $<TARGET_OBJECTS:blowfish>
-  $<TARGET_OBJECTS:cast>
-  $<TARGET_OBJECTS:cfb>
-  $<TARGET_OBJECTS:des_decrepit>
-  $<TARGET_OBJECTS:dh_decrepit>
-  $<TARGET_OBJECTS:dsa_decrepit>
-  $<TARGET_OBJECTS:evp_decrepit>
-  $<TARGET_OBJECTS:obj_decrepit>
-  $<TARGET_OBJECTS:rc4_decrepit>
-  $<TARGET_OBJECTS:ripemd_decrepit>
-  $<TARGET_OBJECTS:rsa_decrepit>
-  $<TARGET_OBJECTS:ssl_decrepit>
-  $<TARGET_OBJECTS:x509_decrepit>
-  $<TARGET_OBJECTS:xts>
+  bio/base64_bio.c
+  blowfish/blowfish.c
+  cast/cast.c
+  cast/cast_tables.c
+  cfb/cfb.c
+  des/cfb64ede.c
+  dh/dh_decrepit.c
+  dsa/dsa_decrepit.c
+  evp/dss1.c
+  evp/evp_do_all.c
+  obj/obj_decrepit.c
+  rc4/rc4_decrepit.c
+  ripemd/ripemd.c
+  rsa/rsa_decrepit.c
+  ssl/ssl_decrepit.c
+  x509/x509_decrepit.c
+  xts/xts.c
 )
 
+add_dependencies(decrepit global_target)
+
 target_link_libraries(decrepit crypto ssl)
 
 add_executable(
@@ -48,6 +36,8 @@
   $<TARGET_OBJECTS:test_support>
 )
 
+add_dependencies(decrepit_test global_target)
+
 target_link_libraries(decrepit_test crypto decrepit boringssl_gtest)
 if(WIN32)
   target_link_libraries(decrepit_test ws2_32)
diff --git a/src/decrepit/bio/CMakeLists.txt b/src/decrepit/bio/CMakeLists.txt
deleted file mode 100644
index 95d9231..0000000
--- a/src/decrepit/bio/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  bio_decrepit
-
-  OBJECT
-
-  base64_bio.c
-)
diff --git a/src/decrepit/blowfish/CMakeLists.txt b/src/decrepit/blowfish/CMakeLists.txt
deleted file mode 100644
index 29729c4..0000000
--- a/src/decrepit/blowfish/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  blowfish
-
-  OBJECT
-
-  blowfish.c
-)
diff --git a/src/decrepit/cast/CMakeLists.txt b/src/decrepit/cast/CMakeLists.txt
deleted file mode 100644
index 2830381..0000000
--- a/src/decrepit/cast/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  cast
-
-  OBJECT
-
-  cast.c
-  cast_tables.c
-)
diff --git a/src/decrepit/cfb/CMakeLists.txt b/src/decrepit/cfb/CMakeLists.txt
deleted file mode 100644
index 63a0ca5..0000000
--- a/src/decrepit/cfb/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  cfb
-
-  OBJECT
-
-  cfb.c
-)
diff --git a/src/decrepit/des/CMakeLists.txt b/src/decrepit/des/CMakeLists.txt
deleted file mode 100644
index 0ee5c2e..0000000
--- a/src/decrepit/des/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  des_decrepit
-
-  OBJECT
-
-  cfb64ede.c
-)
diff --git a/src/decrepit/dh/CMakeLists.txt b/src/decrepit/dh/CMakeLists.txt
deleted file mode 100644
index fb862b5..0000000
--- a/src/decrepit/dh/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  dh_decrepit
-
-  OBJECT
-
-  dh_decrepit.c
-)
diff --git a/src/decrepit/dsa/CMakeLists.txt b/src/decrepit/dsa/CMakeLists.txt
deleted file mode 100644
index cc64b60..0000000
--- a/src/decrepit/dsa/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  dsa_decrepit
-
-  OBJECT
-
-  dsa_decrepit.c
-)
diff --git a/src/decrepit/evp/CMakeLists.txt b/src/decrepit/evp/CMakeLists.txt
deleted file mode 100644
index e631a9a..0000000
--- a/src/decrepit/evp/CMakeLists.txt
+++ /dev/null
@@ -1,10 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  evp_decrepit
-
-  OBJECT
-
-  dss1.c
-  evp_do_all.c
-)
diff --git a/src/decrepit/obj/CMakeLists.txt b/src/decrepit/obj/CMakeLists.txt
deleted file mode 100644
index caaecd3..0000000
--- a/src/decrepit/obj/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  obj_decrepit
-
-  OBJECT
-
-  obj_decrepit.c
-)
diff --git a/src/decrepit/rc4/CMakeLists.txt b/src/decrepit/rc4/CMakeLists.txt
deleted file mode 100644
index 459b24e..0000000
--- a/src/decrepit/rc4/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  rc4_decrepit
-
-  OBJECT
-
-  rc4_decrepit.c
-)
diff --git a/src/decrepit/ripemd/CMakeLists.txt b/src/decrepit/ripemd/CMakeLists.txt
deleted file mode 100644
index d3dd284..0000000
--- a/src/decrepit/ripemd/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  ripemd_decrepit
-
-  OBJECT
-
-  ripemd.c
-)
diff --git a/src/decrepit/rsa/CMakeLists.txt b/src/decrepit/rsa/CMakeLists.txt
deleted file mode 100644
index 66d836b..0000000
--- a/src/decrepit/rsa/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  rsa_decrepit
-
-  OBJECT
-
-  rsa_decrepit.c
-)
diff --git a/src/decrepit/rsa/rsa_decrepit.c b/src/decrepit/rsa/rsa_decrepit.c
index c4ef5b6..54be9b2 100644
--- a/src/decrepit/rsa/rsa_decrepit.c
+++ b/src/decrepit/rsa/rsa_decrepit.c
@@ -85,13 +85,13 @@
   return NULL;
 }
 
-int RSA_padding_add_PKCS1_PSS(RSA *rsa, uint8_t *EM, const uint8_t *mHash,
+int RSA_padding_add_PKCS1_PSS(const RSA *rsa, uint8_t *EM, const uint8_t *mHash,
                               const EVP_MD *Hash, int sLen) {
   return RSA_padding_add_PKCS1_PSS_mgf1(rsa, EM, mHash, Hash, NULL, sLen);
 }
 
-int RSA_verify_PKCS1_PSS(RSA *rsa, const uint8_t *mHash, const EVP_MD *Hash,
-                         const uint8_t *EM, int sLen) {
+int RSA_verify_PKCS1_PSS(const RSA *rsa, const uint8_t *mHash,
+                         const EVP_MD *Hash, const uint8_t *EM, int sLen) {
   return RSA_verify_PKCS1_PSS_mgf1(rsa, mHash, Hash, NULL, EM, sLen);
 }
 
diff --git a/src/decrepit/ssl/CMakeLists.txt b/src/decrepit/ssl/CMakeLists.txt
deleted file mode 100644
index fba0234..0000000
--- a/src/decrepit/ssl/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  ssl_decrepit
-
-  OBJECT
-
-  ssl_decrepit.c
-)
diff --git a/src/decrepit/x509/CMakeLists.txt b/src/decrepit/x509/CMakeLists.txt
deleted file mode 100644
index 930912d..0000000
--- a/src/decrepit/x509/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  x509_decrepit
-
-  OBJECT
-
-  x509_decrepit.c
-)
diff --git a/src/decrepit/x509/x509_decrepit.c b/src/decrepit/x509/x509_decrepit.c
index 5714b40..28015f3 100644
--- a/src/decrepit/x509/x509_decrepit.c
+++ b/src/decrepit/x509/x509_decrepit.c
@@ -16,27 +16,7 @@
 #include <openssl/x509v3.h>
 
 
-X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf,
-                                    X509V3_CTX *ctx, int ext_nid, char *value) {
-  CONF *nconf = NULL;
-  LHASH_OF(CONF_VALUE) *orig_data = NULL;
-
-  if (conf != NULL) {
-    nconf = NCONF_new(NULL /* no method */);
-    if (nconf == NULL) {
-      return NULL;
-    }
-
-    orig_data = nconf->data;
-    nconf->data = conf;
-  }
-
-  X509_EXTENSION *ret = X509V3_EXT_nconf_nid(nconf, ctx, ext_nid, value);
-
-  if (nconf != NULL) {
-    nconf->data = orig_data;
-    NCONF_free(nconf);
-  }
-
-  return ret;
+X509_EXTENSION *X509V3_EXT_conf_nid(X509_MUST_BE_NULL *conf, X509V3_CTX *ctx,
+                                    int ext_nid, char *value) {
+  return X509V3_EXT_nconf_nid(NULL, ctx, ext_nid, value);
 }
diff --git a/src/decrepit/xts/CMakeLists.txt b/src/decrepit/xts/CMakeLists.txt
deleted file mode 100644
index 7dccde0..0000000
--- a/src/decrepit/xts/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  xts
-
-  OBJECT
-
-  xts.c
-)
diff --git a/src/decrepit/xts/xts.c b/src/decrepit/xts/xts.c
index e3189e5..bc2a1b2 100644
--- a/src/decrepit/xts/xts.c
+++ b/src/decrepit/xts/xts.c
@@ -57,7 +57,7 @@
 
 
 typedef struct xts128_context {
-  void *key1, *key2;
+  AES_KEY *key1, *key2;
   block128_f block1, block2;
 } XTS128_CONTEXT;
 
@@ -186,20 +186,20 @@
     // key_len is two AES keys
     if (enc) {
       AES_set_encrypt_key(key, ctx->key_len * 4, &xctx->ks1.ks);
-      xctx->xts.block1 = (block128_f) AES_encrypt;
+      xctx->xts.block1 = AES_encrypt;
     } else {
       AES_set_decrypt_key(key, ctx->key_len * 4, &xctx->ks1.ks);
-      xctx->xts.block1 = (block128_f) AES_decrypt;
+      xctx->xts.block1 = AES_decrypt;
     }
 
     AES_set_encrypt_key(key + ctx->key_len / 2,
                         ctx->key_len * 4, &xctx->ks2.ks);
-    xctx->xts.block2 = (block128_f) AES_encrypt;
-    xctx->xts.key1 = &xctx->ks1;
+    xctx->xts.block2 = AES_encrypt;
+    xctx->xts.key1 = &xctx->ks1.ks;
   }
 
   if (iv) {
-    xctx->xts.key2 = &xctx->ks2;
+    xctx->xts.key2 = &xctx->ks2.ks;
     OPENSSL_memcpy(ctx->iv, iv, 16);
   }
 
@@ -226,16 +226,16 @@
     EVP_CIPHER_CTX *out = ptr;
     EVP_AES_XTS_CTX *xctx_out = out->cipher_data;
     if (xctx->xts.key1) {
-      if (xctx->xts.key1 != &xctx->ks1) {
+      if (xctx->xts.key1 != &xctx->ks1.ks) {
         return 0;
       }
-      xctx_out->xts.key1 = &xctx_out->ks1;
+      xctx_out->xts.key1 = &xctx_out->ks1.ks;
     }
     if (xctx->xts.key2) {
-      if (xctx->xts.key2 != &xctx->ks2) {
+      if (xctx->xts.key2 != &xctx->ks2.ks) {
         return 0;
       }
-      xctx_out->xts.key2 = &xctx_out->ks2;
+      xctx_out->xts.key2 = &xctx_out->ks2.ks;
     }
     return 1;
   } else if (type != EVP_CTRL_INIT) {
diff --git a/src/fipstools/CMakeLists.txt b/src/fipstools/CMakeLists.txt
index f0f7b2c..779fcd1 100644
--- a/src/fipstools/CMakeLists.txt
+++ b/src/fipstools/CMakeLists.txt
@@ -29,6 +29,8 @@
     $<TARGET_OBJECTS:test_support>
   )
 
+  add_dependencies(cavp global_target)
+
   add_executable(
     test_fips
 
@@ -36,6 +38,8 @@
     $<TARGET_OBJECTS:test_support>
   )
 
+  add_dependencies(test_fips global_target)
+
   target_link_libraries(cavp crypto)
   target_link_libraries(test_fips crypto)
 endif()
diff --git a/src/fuzz/CMakeLists.txt b/src/fuzz/CMakeLists.txt
index a269696..03d0326 100644
--- a/src/fuzz/CMakeLists.txt
+++ b/src/fuzz/CMakeLists.txt
@@ -2,44 +2,24 @@
 
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-prototypes")
 
-add_executable(bn_mod_exp bn_mod_exp.cc)
-target_link_libraries(bn_mod_exp Fuzzer crypto)
+macro(fuzzer name)
+  add_executable(${name} ${name}.cc)
+  add_dependencies(${name} global_target)
+  target_link_libraries(${name} crypto ${ARGN})
+  set_target_properties(${name} PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
+endmacro()
 
-add_executable(bn_div bn_div.cc)
-target_link_libraries(bn_div Fuzzer crypto)
-
-add_executable(privkey privkey.cc)
-target_link_libraries(privkey Fuzzer crypto)
-
-add_executable(cert cert.cc)
-target_link_libraries(cert Fuzzer crypto)
-
-add_executable(spki spki.cc)
-target_link_libraries(spki Fuzzer crypto)
-
-add_executable(pkcs8 pkcs8.cc)
-target_link_libraries(pkcs8 Fuzzer crypto)
-
-add_executable(pkcs12 pkcs12.cc)
-target_link_libraries(pkcs12 Fuzzer crypto)
-
-add_executable(server server.cc)
-target_link_libraries(server Fuzzer crypto ssl)
-
-add_executable(client client.cc)
-target_link_libraries(client Fuzzer crypto ssl)
-
-add_executable(dtls_server dtls_server.cc)
-target_link_libraries(dtls_server Fuzzer crypto ssl)
-
-add_executable(dtls_client dtls_client.cc)
-target_link_libraries(dtls_client Fuzzer crypto ssl)
-
-add_executable(read_pem read_pem.cc)
-target_link_libraries(read_pem Fuzzer crypto)
-
-add_executable(ssl_ctx_api ssl_ctx_api.cc)
-target_link_libraries(ssl_ctx_api Fuzzer crypto ssl)
-
-add_executable(session session.cc)
-target_link_libraries(session Fuzzer crypto ssl)
+fuzzer(arm_cpuinfo)
+fuzzer(bn_mod_exp)
+fuzzer(privkey)
+fuzzer(cert)
+fuzzer(spki)
+fuzzer(pkcs8)
+fuzzer(pkcs12)
+fuzzer(read_pem)
+fuzzer(server ssl)
+fuzzer(client ssl)
+fuzzer(dtls_server ssl)
+fuzzer(dtls_client ssl)
+fuzzer(ssl_ctx_api ssl)
+fuzzer(session ssl)
diff --git a/src/fuzz/arm_cpuinfo.cc b/src/fuzz/arm_cpuinfo.cc
new file mode 100644
index 0000000..5d70046
--- /dev/null
+++ b/src/fuzz/arm_cpuinfo.cc
@@ -0,0 +1,24 @@
+/* Copyright (c) 2018, 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. */
+
+#include "../crypto/cpu-arm-linux.h"
+
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) {
+  STRING_PIECE sp = {reinterpret_cast<const char *>(buf), len};
+  crypto_get_arm_hwcap_from_cpuinfo(&sp);
+  crypto_get_arm_hwcap2_from_cpuinfo(&sp);
+  crypto_cpuinfo_has_broken_neon(&sp);
+  return 0;
+}
diff --git a/src/fuzz/arm_cpuinfo_corpus/0b628cc4b99b6b797a0486c67a6ff6fa7d24b3b4 b/src/fuzz/arm_cpuinfo_corpus/0b628cc4b99b6b797a0486c67a6ff6fa7d24b3b4
new file mode 100644
index 0000000..c95dfd3
--- /dev/null
+++ b/src/fuzz/arm_cpuinfo_corpus/0b628cc4b99b6b797a0486c67a6ff6fa7d24b3b4
@@ -0,0 +1,17 @@
+Processor: ARMv7 Processory rev 0 (v71)
+processor: 0
+BogoMIPS: 13.50
+
+Processor: 1
+BogoMIPS: 13.50
+
+Features: swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt
+CPU implementer : 0x51
+CPU architecture: 7
+CPU variant: 0x1
+CPU part: 0x04d
+CPU revision: 0
+
+Hardware: SAMSUNG M2
+Revision: 0010
+Serial: 00001e030000354e
diff --git a/src/fuzz/arm_cpuinfo_corpus/27cdc19e630f4174c00376cdc51b1c556723b0e1 b/src/fuzz/arm_cpuinfo_corpus/27cdc19e630f4174c00376cdc51b1c556723b0e1
new file mode 100644
index 0000000..89b9a0c
--- /dev/null
+++ b/src/fuzz/arm_cpuinfo_corpus/27cdc19e630f4174c00376cdc51b1c556723b0e1
@@ -0,0 +1,14 @@
+Processor       : ARMv7 Processor rev 0 (v7l)
+processor       : 0
+BogoMIPS        : 13.53
+
+Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
+CPU implementer : 0x51
+CPU architecture: 7
+CPU variant     : 0x1
+CPU part        : 0x04d
+CPU revision    : 0
+
+Hardware        : SAMSUNG M2_ATT
+Revision        : 0010
+Serial          : 0000df0c00004d4c
diff --git a/src/fuzz/arm_cpuinfo_corpus/8e90793faafbdfa30081e963e45fff08a360dc75 b/src/fuzz/arm_cpuinfo_corpus/8e90793faafbdfa30081e963e45fff08a360dc75
new file mode 100644
index 0000000..4709eb2
--- /dev/null
+++ b/src/fuzz/arm_cpuinfo_corpus/8e90793faafbdfa30081e963e45fff08a360dc75
@@ -0,0 +1,14 @@
+Processor       : ARMv7 Processor rev 4 (v7l)
+processor       : 0
+BogoMIPS        : 13.53
+
+Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4
+CPU implementer : 0x51
+CPU architecture: 7
+CPU variant     : 0x1
+CPU part        : 0x04d
+CPU revision    : 4
+
+Hardware        : msm8960dt
+Revision        : 82a0
+Serial          : 0001000201fe37a5
diff --git a/src/fuzz/arm_cpuinfo_corpus/d8895d2c91f858239b2670eb211af78a71d75d02 b/src/fuzz/arm_cpuinfo_corpus/d8895d2c91f858239b2670eb211af78a71d75d02
new file mode 100644
index 0000000..60a5409
--- /dev/null
+++ b/src/fuzz/arm_cpuinfo_corpus/d8895d2c91f858239b2670eb211af78a71d75d02
@@ -0,0 +1,74 @@
+Processor	: AArch64 Processor rev 1 (aarch64)
+processor	: 0
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0xa
+CPU part	: 0x801
+CPU revision	: 4
+
+processor	: 1
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0xa
+CPU part	: 0x801
+CPU revision	: 4
+
+processor	: 2
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0xa
+CPU part	: 0x801
+CPU revision	: 4
+
+processor	: 3
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0xa
+CPU part	: 0x801
+CPU revision	: 4
+
+processor	: 4
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0xa
+CPU part	: 0x800
+CPU revision	: 1
+
+processor	: 5
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0xa
+CPU part	: 0x800
+CPU revision	: 1
+
+processor	: 6
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0xa
+CPU part	: 0x800
+CPU revision	: 1
+
+processor	: 7
+BogoMIPS	: 38.00
+Features	: fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer	: 0x51
+CPU architecture: 8
+CPU variant	: 0xa
+CPU part	: 0x800
+CPU revision	: 1
+
+Hardware	: Qualcomm Technologies, Inc MSM8998
diff --git a/src/fuzz/arm_cpuinfo_corpus/dfa633d05f10f2cb5c32b0a767efd10bf36cf3be b/src/fuzz/arm_cpuinfo_corpus/dfa633d05f10f2cb5c32b0a767efd10bf36cf3be
new file mode 100644
index 0000000..cfb8017
--- /dev/null
+++ b/src/fuzz/arm_cpuinfo_corpus/dfa633d05f10f2cb5c32b0a767efd10bf36cf3be
@@ -0,0 +1,23 @@
+Processor       : ARMv7 Processor rev 2 (v7l)
+processor       : 0
+BogoMIPS        : 13.53
+
+processor       : 1
+BogoMIPS        : 13.53
+
+processor       : 2
+BogoMIPS        : 13.53
+
+processor       : 3
+BogoMIPS        : 13.53
+
+Features        : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 
+CPU implementer : 0x51
+CPU architecture: 7
+CPU variant     : 0x0
+CPU part        : 0x06f
+CPU revision    : 2
+
+Hardware        : QCT APQ8064 MAKO
+Revision        : 000b
+Serial          : 0000000000000000
diff --git a/src/go.mod b/src/go.mod
new file mode 100644
index 0000000..09d36fb
--- /dev/null
+++ b/src/go.mod
@@ -0,0 +1 @@
+module boringssl.googlesource.com/boringssl
diff --git a/src/include/openssl/aead.h b/src/include/openssl/aead.h
index af31554..f19344e 100644
--- a/src/include/openssl/aead.h
+++ b/src/include/openssl/aead.h
@@ -425,7 +425,7 @@
 #if !defined(BORINGSSL_NO_CXX)
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 using ScopedEVP_AEAD_CTX =
     internal::StackAllocated<EVP_AEAD_CTX, void, EVP_AEAD_CTX_zero,
@@ -433,7 +433,7 @@
 
 BORINGSSL_MAKE_DELETER(EVP_AEAD_CTX, EVP_AEAD_CTX_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 #endif
diff --git a/src/include/openssl/asn1.h b/src/include/openssl/asn1.h
index f7b6b86..8b61eaa 100644
--- a/src/include/openssl/asn1.h
+++ b/src/include/openssl/asn1.h
@@ -298,19 +298,6 @@
 	OPENSSL_EXPORT int fname##_print_ctx(BIO *out, stname *x, int indent, \
 					 const ASN1_PCTX *pctx);
 
-#define D2I_OF(type) type *(*)(type **,const unsigned char **,long)
-#define I2D_OF(type) int (*)(type *,unsigned char **)
-#define I2D_OF_const(type) int (*)(const type *,unsigned char **)
-
-#define CHECKED_D2I_OF(type, d2i) \
-    ((d2i_of_void*) (1 ? d2i : ((D2I_OF(type))0)))
-#define CHECKED_I2D_OF(type, i2d) \
-    ((i2d_of_void*) (1 ? i2d : ((I2D_OF(type))0)))
-#define CHECKED_NEW_OF(type, xnew) \
-    ((void *(*)(void)) (1 ? xnew : ((type *(*)(void))0)))
-#define CHECKED_PPTR_OF(type, p) \
-    ((void**) (1 ? p : (type**)0))
-
 typedef void *d2i_of_void(void **, const unsigned char **, long);
 typedef int i2d_of_void(const void *, unsigned char **);
 
@@ -762,76 +749,17 @@
 OPENSSL_EXPORT int ASN1_put_eoc(unsigned char **pp);
 OPENSSL_EXPORT int ASN1_object_size(int constructed, int length, int tag);
 
-/* Used to implement other functions */
-OPENSSL_EXPORT void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *x);
-
-#define ASN1_dup_of(type,i2d,d2i,x) \
-    ((type*)ASN1_dup(CHECKED_I2D_OF(type, i2d), \
-		     CHECKED_D2I_OF(type, d2i), \
-		     CHECKED_PTR_OF(type, x)))
-
-#define ASN1_dup_of_const(type,i2d,d2i,x) \
-    ((type*)ASN1_dup(CHECKED_I2D_OF(const type, i2d), \
-		     CHECKED_D2I_OF(type, d2i), \
-		     CHECKED_PTR_OF(const type, x)))
-
 OPENSSL_EXPORT void *ASN1_item_dup(const ASN1_ITEM *it, void *x);
 
-/* ASN1 alloc/free macros for when a type is only used internally */
-
-#define M_ASN1_new_of(type) (type *)ASN1_item_new(ASN1_ITEM_rptr(type))
-#define M_ASN1_free_of(x, type) \
-		ASN1_item_free(CHECKED_PTR_OF(type, x), ASN1_ITEM_rptr(type))
-
 #ifndef OPENSSL_NO_FP_API
-OPENSSL_EXPORT void *ASN1_d2i_fp(void *(*xnew)(void), d2i_of_void *d2i, FILE *in, void **x);
-
-#define ASN1_d2i_fp_of(type,xnew,d2i,in,x) \
-    ((type*)ASN1_d2i_fp(CHECKED_NEW_OF(type, xnew), \
-			CHECKED_D2I_OF(type, d2i), \
-			in, \
-			CHECKED_PPTR_OF(type, x)))
-
 OPENSSL_EXPORT void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x);
-OPENSSL_EXPORT int ASN1_i2d_fp(i2d_of_void *i2d,FILE *out,void *x);
-
-#define ASN1_i2d_fp_of(type,i2d,out,x) \
-    (ASN1_i2d_fp(CHECKED_I2D_OF(type, i2d), \
-		 out, \
-		 CHECKED_PTR_OF(type, x)))
-
-#define ASN1_i2d_fp_of_const(type,i2d,out,x) \
-    (ASN1_i2d_fp(CHECKED_I2D_OF(const type, i2d), \
-		 out, \
-		 CHECKED_PTR_OF(const type, 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, ASN1_STRING *str, unsigned long flags);
 #endif
 
 OPENSSL_EXPORT int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in);
 
-OPENSSL_EXPORT void *ASN1_d2i_bio(void *(*xnew)(void), d2i_of_void *d2i, BIO *in, void **x);
-
-#define ASN1_d2i_bio_of(type,xnew,d2i,in,x) \
-    ((type*)ASN1_d2i_bio( CHECKED_NEW_OF(type, xnew), \
-			  CHECKED_D2I_OF(type, d2i), \
-			  in, \
-			  CHECKED_PPTR_OF(type, x)))
-
 OPENSSL_EXPORT void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x);
-OPENSSL_EXPORT int ASN1_i2d_bio(i2d_of_void *i2d,BIO *out, void *x);
-
-#define ASN1_i2d_bio_of(type,i2d,out,x) \
-    (ASN1_i2d_bio(CHECKED_I2D_OF(type, i2d), \
-		  out, \
-		  CHECKED_PTR_OF(type, x)))
-
-#define ASN1_i2d_bio_of_const(type,i2d,out,x) \
-    (ASN1_i2d_bio(CHECKED_I2D_OF(const type, i2d), \
-		  out, \
-		  CHECKED_PTR_OF(const type, 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);
@@ -875,13 +803,13 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(ASN1_OBJECT, ASN1_OBJECT_free)
 BORINGSSL_MAKE_DELETER(ASN1_STRING, ASN1_STRING_free)
 BORINGSSL_MAKE_DELETER(ASN1_TYPE, ASN1_TYPE_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  /* extern C++ */
 
diff --git a/src/include/openssl/base.h b/src/include/openssl/base.h
index 8bfc2da..5a4bb66 100644
--- a/src/include/openssl/base.h
+++ b/src/include/openssl/base.h
@@ -65,12 +65,20 @@
 #include <stdio.h>
 #endif
 
+#if defined(__APPLE__)
+#include <TargetConditionals.h>
+#endif
+
 // Include a BoringSSL-only header so consumers including this header without
 // setting up include paths do not accidentally pick up the system
 // opensslconf.h.
 #include <openssl/is_boringssl.h>
 #include <openssl/opensslconf.h>
 
+#if defined(BORINGSSL_PREFIX)
+#include <boringssl_prefix_symbols.h>
+#endif
+
 #if defined(__cplusplus)
 extern "C" {
 #endif
@@ -116,6 +124,9 @@
 
 #if defined(__APPLE__)
 #define OPENSSL_APPLE
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#define OPENSSL_IOS
+#endif
 #endif
 
 #if defined(_WIN32)
@@ -132,14 +143,36 @@
 
 #if defined(TRUSTY)
 #define OPENSSL_TRUSTY
-#define OPENSSL_NO_THREADS
+#define OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED
 #endif
 
 #if defined(__ANDROID_API__)
 #define OPENSSL_ANDROID
 #endif
 
-#if !defined(OPENSSL_NO_THREADS)
+// OPENSSL_NO_THREADS has been deprecated in favor of this much longer and
+// louder name, to better reflect exactly what that option did.
+//
+// TODO(davidben): Remove this block when callers have migrated.
+#if defined(OPENSSL_NO_THREADS) && \
+    !defined(OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED)
+#define OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED
+#endif
+
+// BoringSSL requires platform's locking APIs to make internal global state
+// thread-safe, including the PRNG. On some single-threaded embedded platforms,
+// locking APIs may not exist, so this dependency may be disabled with the
+// following build flag.
+//
+// IMPORTANT: Doing so means the consumer promises the library will never be
+// used in any multi-threaded context. It causes BoringSSL to be globally
+// thread-unsafe. Setting it inappropriately will subtly and unpredictably
+// corrupt memory and leak secret keys.
+//
+// Do not set this flag on any platform where threads are possible. BoringSSL
+// maintainers will not provide support for any consumers that do so. Changes
+// which break such unsupported configurations will not be reverted.
+#if !defined(OPENSSL_NO_THREADS_CORRUPT_MEMORY_AND_LEAK_SECRETS_IF_THREADED)
 #define OPENSSL_THREADS
 #endif
 
@@ -213,6 +246,35 @@
 #define OPENSSL_UNUSED
 #endif
 
+// C and C++ handle inline functions differently. In C++, an inline function is
+// defined in just the header file, potentially emitted in multiple compilation
+// units (in cases the compiler did not inline), but each copy must be identical
+// to satsify ODR. In C, a non-static inline must be manually emitted in exactly
+// one compilation unit with a separate extern inline declaration.
+//
+// In both languages, exported inline functions referencing file-local symbols
+// are problematic. C forbids this altogether (though GCC and Clang seem not to
+// enforce it). It works in C++, but ODR requires the definitions be identical,
+// including all names in the definitions resolving to the "same entity". In
+// practice, this is unlikely to be a problem, but an inline function that
+// returns a pointer to a file-local symbol
+// could compile oddly.
+//
+// Historically, we used static inline in headers. However, to satisfy ODR, use
+// plain inline in C++, to allow inline consumer functions to call our header
+// functions. Plain inline would also work better with C99 inline, but that is
+// not used much in practice, extern inline is tedious, and there are conflicts
+// with the old gnu89 model:
+// https://stackoverflow.com/questions/216510/extern-inline
+#if defined(__cplusplus)
+#define OPENSSL_INLINE inline
+#else
+// Add OPENSSL_UNUSED so that, should an inline function be emitted via macro
+// (e.g. a |STACK_OF(T)| implementation) in a source file without tripping
+// clang's -Wunused-function.
+#define OPENSSL_INLINE static inline OPENSSL_UNUSED
+#endif
+
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE) && \
     !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
 #define BORINGSSL_UNSAFE_DETERMINISTIC_MODE
@@ -366,6 +428,18 @@
 #define BORINGSSL_NO_CXX
 #endif
 
+#if defined(BORINGSSL_PREFIX)
+#define BSSL_NAMESPACE_BEGIN \
+  namespace bssl {           \
+  inline namespace BORINGSSL_PREFIX {
+#define BSSL_NAMESPACE_END \
+  }                        \
+  }
+#else
+#define BSSL_NAMESPACE_BEGIN namespace bssl {
+#define BSSL_NAMESPACE_END }
+#endif
+
 // MSVC doesn't set __cplusplus to 201103 to indicate C++11 support (see
 // https://connect.microsoft.com/VisualStudio/feedback/details/763051/a-value-of-predefined-macro-cplusplus-is-still-199711l)
 // so MSVC is just assumed to support C++11.
@@ -374,6 +448,7 @@
 #endif
 
 #if !defined(BORINGSSL_NO_CXX)
+
 extern "C++" {
 
 #include <memory>
@@ -395,7 +470,7 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 namespace internal {
 
@@ -460,19 +535,19 @@
 template <typename T>
 using UniquePtr = std::unique_ptr<T, internal::Deleter<T>>;
 
-#define BORINGSSL_MAKE_UP_REF(type, up_ref_func)                    \
-  static inline UniquePtr<type> UpRef(type *v) {                    \
-    if (v != nullptr) {                                             \
-      up_ref_func(v);                                               \
-    }                                                               \
-    return UniquePtr<type>(v);                                      \
-  }                                                                 \
-                                                                    \
-  static inline UniquePtr<type> UpRef(const UniquePtr<type> &ptr) { \
-    return UpRef(ptr.get());                                        \
+#define BORINGSSL_MAKE_UP_REF(type, up_ref_func)             \
+  inline UniquePtr<type> UpRef(type *v) {                    \
+    if (v != nullptr) {                                      \
+      up_ref_func(v);                                        \
+    }                                                        \
+    return UniquePtr<type>(v);                               \
+  }                                                          \
+                                                             \
+  inline UniquePtr<type> UpRef(const UniquePtr<type> &ptr) { \
+    return UpRef(ptr.get());                                 \
   }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/base64.h b/src/include/openssl/base64.h
index ef76088..c88546d 100644
--- a/src/include/openssl/base64.h
+++ b/src/include/openssl/base64.h
@@ -67,7 +67,10 @@
 // base64 functions.
 //
 // For historical reasons, these functions have the EVP_ prefix but just do
-// base64 encoding and decoding.
+// base64 encoding and decoding. Note that BoringSSL is a cryptography library,
+// so these functions are implemented with side channel protections, at a
+// performance cost. For other base64 uses, use a general-purpose base64
+// implementation.
 
 
 // Encoding
diff --git a/src/include/openssl/bio.h b/src/include/openssl/bio.h
index adb641b..8e2db65 100644
--- a/src/include/openssl/bio.h
+++ b/src/include/openssl/bio.h
@@ -117,10 +117,14 @@
 // return a line for this call, remove the warning above.
 OPENSSL_EXPORT int BIO_gets(BIO *bio, char *buf, int size);
 
-// BIO_write writes |len| bytes from |data| to BIO. It returns the number of
+// BIO_write writes |len| bytes from |data| to |bio|. It returns the number of
 // bytes written or a negative number on error.
 OPENSSL_EXPORT int BIO_write(BIO *bio, const void *data, int len);
 
+// BIO_write_all writes |len| bytes from |data| to |bio|, looping as necessary.
+// It returns one if all bytes were successfully written and zero on error.
+OPENSSL_EXPORT int BIO_write_all(BIO *bio, const void *data, size_t len);
+
 // BIO_puts writes a NUL terminated string from |buf| to |bio|. It returns the
 // number of bytes written or a negative number on error.
 OPENSSL_EXPORT int BIO_puts(BIO *bio, const char *buf);
@@ -677,26 +681,49 @@
 OPENSSL_EXPORT int BIO_get_init(BIO *bio);
 
 // These are values of the |cmd| argument to |BIO_ctrl|.
-#define BIO_CTRL_RESET		1  // opt - rewind/zero etc
-#define BIO_CTRL_EOF		2  // opt - are we at the eof
-#define BIO_CTRL_INFO		3  // opt - extra tit-bits
-#define BIO_CTRL_SET		4  // man - set the 'IO' type
-#define BIO_CTRL_GET		5  // man - get the 'IO' type
-#define BIO_CTRL_PUSH	6
-#define BIO_CTRL_POP	7
-#define BIO_CTRL_GET_CLOSE	8  // man - set the 'close' on free
-#define BIO_CTRL_SET_CLOSE	9  // man - set the 'close' on free
-#define BIO_CTRL_PENDING	10  // opt - is their more data buffered
-#define BIO_CTRL_FLUSH		11  // opt - 'flush' buffered output
-#define BIO_CTRL_WPENDING	13  // opt - number of bytes still to write
-// callback is int cb(BIO *bio,state,ret);
-#define BIO_CTRL_SET_CALLBACK	14  // opt - set callback function
-#define BIO_CTRL_GET_CALLBACK	15  // opt - set callback function
-#define BIO_CTRL_SET_FILENAME	30	  // BIO_s_file special
 
-// BIO_CTRL_DUP is never used, but exists to allow code to compile more
-// easily.
-#define BIO_CTRL_DUP	12
+// BIO_CTRL_RESET implements |BIO_reset|. The arguments are unused.
+#define BIO_CTRL_RESET 1
+
+// BIO_CTRL_EOF implements |BIO_eof|. The arguments are unused.
+#define BIO_CTRL_EOF 2
+
+// BIO_CTRL_INFO is a legacy command that returns information specific to the
+// type of |BIO|. It is not safe to call generically and should not be
+// implemented in new |BIO| types.
+#define BIO_CTRL_INFO 3
+
+// BIO_CTRL_GET_CLOSE returns the close flag set by |BIO_CTRL_SET_CLOSE|. The
+// arguments are unused.
+#define BIO_CTRL_GET_CLOSE 8
+
+// BIO_CTRL_SET_CLOSE implements |BIO_set_close|. The |larg| argument is the
+// close flag.
+#define BIO_CTRL_SET_CLOSE 9
+
+// BIO_CTRL_PENDING implements |BIO_pending|. The arguments are unused.
+#define BIO_CTRL_PENDING 10
+
+// BIO_CTRL_FLUSH implements |BIO_flush|. The arguments are unused.
+#define BIO_CTRL_FLUSH 11
+
+// BIO_CTRL_WPENDING implements |BIO_wpending|. The arguments are unused.
+#define BIO_CTRL_WPENDING 13
+
+// BIO_CTRL_SET_CALLBACK sets an informational callback of type
+// int cb(BIO *bio, int state, int ret)
+#define BIO_CTRL_SET_CALLBACK 14
+
+// BIO_CTRL_GET_CALLBACK returns the callback set by |BIO_CTRL_SET_CALLBACK|.
+#define BIO_CTRL_GET_CALLBACK 15
+
+// The following are never used, but are defined to aid porting existing code.
+#define BIO_CTRL_SET 4
+#define BIO_CTRL_GET 5
+#define BIO_CTRL_PUSH 6
+#define BIO_CTRL_POP 7
+#define BIO_CTRL_DUP 12
+#define BIO_CTRL_SET_FILENAME 30
 
 
 // Deprecated functions.
@@ -706,6 +733,8 @@
 // |BIO_flush| when done writing, to signal that no more data are to be
 // encoded. The flag |BIO_FLAGS_BASE64_NO_NL| may be set to encode all the data
 // on one line.
+//
+// Use |EVP_EncodeBlock| and |EVP_DecodeBase64| instead.
 OPENSSL_EXPORT const BIO_METHOD *BIO_f_base64(void);
 
 OPENSSL_EXPORT void BIO_set_retry_special(BIO *bio);
@@ -733,8 +762,8 @@
 #define BIO_FLAGS_RWS (BIO_FLAGS_READ | BIO_FLAGS_WRITE | BIO_FLAGS_IO_SPECIAL)
 #define BIO_FLAGS_SHOULD_RETRY 0x08
 #define BIO_FLAGS_BASE64_NO_NL 0x100
-// This is used with memory BIOs: it means we shouldn't free up or change the
-// data in any way.
+// BIO_FLAGS_MEM_RDONLY is used with memory BIOs. It means we shouldn't free up
+// or change the data in any way.
 #define BIO_FLAGS_MEM_RDONLY 0x200
 
 // These are the 'types' of BIOs
@@ -762,7 +791,7 @@
 #define BIO_TYPE_ASN1 (22 | 0x0200)  // filter
 #define BIO_TYPE_COMP (23 | 0x0200)  // filter
 
-// |BIO_TYPE_DESCRIPTOR| denotes that the |BIO| responds to the |BIO_C_SET_FD|
+// BIO_TYPE_DESCRIPTOR denotes that the |BIO| responds to the |BIO_C_SET_FD|
 // (|BIO_set_fd|) and |BIO_C_GET_FD| (|BIO_get_fd|) control hooks.
 #define BIO_TYPE_DESCRIPTOR 0x0100  // socket, fd, connect or accept
 #define BIO_TYPE_FILTER 0x0200
@@ -809,61 +838,61 @@
   size_t num_read, num_write;
 };
 
-#define BIO_C_SET_CONNECT			100
-#define BIO_C_DO_STATE_MACHINE			101
-#define BIO_C_SET_NBIO				102
-#define BIO_C_SET_PROXY_PARAM			103
-#define BIO_C_SET_FD				104
-#define BIO_C_GET_FD				105
-#define BIO_C_SET_FILE_PTR			106
-#define BIO_C_GET_FILE_PTR			107
-#define BIO_C_SET_FILENAME			108
-#define BIO_C_SET_SSL				109
-#define BIO_C_GET_SSL				110
-#define BIO_C_SET_MD				111
-#define BIO_C_GET_MD				112
-#define BIO_C_GET_CIPHER_STATUS			113
-#define BIO_C_SET_BUF_MEM			114
-#define BIO_C_GET_BUF_MEM_PTR			115
-#define BIO_C_GET_BUFF_NUM_LINES		116
-#define BIO_C_SET_BUFF_SIZE			117
-#define BIO_C_SET_ACCEPT			118
-#define BIO_C_SSL_MODE				119
-#define BIO_C_GET_MD_CTX			120
-#define BIO_C_GET_PROXY_PARAM			121
-#define BIO_C_SET_BUFF_READ_DATA		122  // data to read first
-#define BIO_C_GET_ACCEPT			124
-#define BIO_C_SET_SSL_RENEGOTIATE_BYTES		125
-#define BIO_C_GET_SSL_NUM_RENEGOTIATES		126
-#define BIO_C_SET_SSL_RENEGOTIATE_TIMEOUT	127
-#define BIO_C_FILE_SEEK				128
-#define BIO_C_GET_CIPHER_CTX			129
-#define BIO_C_SET_BUF_MEM_EOF_RETURN		130  //return end of input value
-#define BIO_C_SET_BIND_MODE			131
-#define BIO_C_GET_BIND_MODE			132
-#define BIO_C_FILE_TELL				133
-#define BIO_C_GET_SOCKS				134
-#define BIO_C_SET_SOCKS				135
+#define BIO_C_SET_CONNECT 100
+#define BIO_C_DO_STATE_MACHINE 101
+#define BIO_C_SET_NBIO 102
+#define BIO_C_SET_PROXY_PARAM 103
+#define BIO_C_SET_FD 104
+#define BIO_C_GET_FD 105
+#define BIO_C_SET_FILE_PTR 106
+#define BIO_C_GET_FILE_PTR 107
+#define BIO_C_SET_FILENAME 108
+#define BIO_C_SET_SSL 109
+#define BIO_C_GET_SSL 110
+#define BIO_C_SET_MD 111
+#define BIO_C_GET_MD 112
+#define BIO_C_GET_CIPHER_STATUS 113
+#define BIO_C_SET_BUF_MEM 114
+#define BIO_C_GET_BUF_MEM_PTR 115
+#define BIO_C_GET_BUFF_NUM_LINES 116
+#define BIO_C_SET_BUFF_SIZE 117
+#define BIO_C_SET_ACCEPT 118
+#define BIO_C_SSL_MODE 119
+#define BIO_C_GET_MD_CTX 120
+#define BIO_C_GET_PROXY_PARAM 121
+#define BIO_C_SET_BUFF_READ_DATA 122  // data to read first
+#define BIO_C_GET_ACCEPT 124
+#define BIO_C_SET_SSL_RENEGOTIATE_BYTES 125
+#define BIO_C_GET_SSL_NUM_RENEGOTIATES 126
+#define BIO_C_SET_SSL_RENEGOTIATE_TIMEOUT 127
+#define BIO_C_FILE_SEEK 128
+#define BIO_C_GET_CIPHER_CTX 129
+#define BIO_C_SET_BUF_MEM_EOF_RETURN 130  // return end of input value
+#define BIO_C_SET_BIND_MODE 131
+#define BIO_C_GET_BIND_MODE 132
+#define BIO_C_FILE_TELL 133
+#define BIO_C_GET_SOCKS 134
+#define BIO_C_SET_SOCKS 135
 
-#define BIO_C_SET_WRITE_BUF_SIZE		136  // for BIO_s_bio
-#define BIO_C_GET_WRITE_BUF_SIZE		137
-#define BIO_C_GET_WRITE_GUARANTEE		140
-#define BIO_C_GET_READ_REQUEST			141
-#define BIO_C_SHUTDOWN_WR			142
-#define BIO_C_NREAD0				143
-#define BIO_C_NREAD				144
-#define BIO_C_NWRITE0				145
-#define BIO_C_NWRITE				146
-#define BIO_C_RESET_READ_REQUEST		147
-#define BIO_C_SET_MD_CTX			148
+#define BIO_C_SET_WRITE_BUF_SIZE 136  // for BIO_s_bio
+#define BIO_C_GET_WRITE_BUF_SIZE 137
+#define BIO_C_GET_WRITE_GUARANTEE 140
+#define BIO_C_GET_READ_REQUEST 141
+#define BIO_C_SHUTDOWN_WR 142
+#define BIO_C_NREAD0 143
+#define BIO_C_NREAD 144
+#define BIO_C_NWRITE0 145
+#define BIO_C_NWRITE 146
+#define BIO_C_RESET_READ_REQUEST 147
+#define BIO_C_SET_MD_CTX 148
 
-#define BIO_C_SET_PREFIX			149
-#define BIO_C_GET_PREFIX			150
-#define BIO_C_SET_SUFFIX			151
-#define BIO_C_GET_SUFFIX			152
+#define BIO_C_SET_PREFIX 149
+#define BIO_C_GET_PREFIX 150
+#define BIO_C_SET_SUFFIX 151
+#define BIO_C_GET_SUFFIX 152
 
-#define BIO_C_SET_EX_ARG			153
-#define BIO_C_GET_EX_ARG			154
+#define BIO_C_SET_EX_ARG 153
+#define BIO_C_GET_EX_ARG 154
 
 
 #if defined(__cplusplus)
@@ -871,12 +900,12 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(BIO, BIO_free)
 BORINGSSL_MAKE_UP_REF(BIO, BIO_up_ref)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index e8cc70a..251c717 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -630,9 +630,12 @@
 // BN_pseudo_rand_range is an alias for BN_rand_range.
 OPENSSL_EXPORT int BN_pseudo_rand_range(BIGNUM *rnd, const BIGNUM *range);
 
-// BN_GENCB holds a callback function that is used by generation functions that
-// can take a very long time to complete. Use |BN_GENCB_set| to initialise a
-// |BN_GENCB| structure.
+#define BN_GENCB_GENERATED 0
+#define BN_GENCB_PRIME_TEST 1
+
+// bn_gencb_st, or |BN_GENCB|, holds a callback function that is used by
+// generation functions that can take a very long time to complete. Use
+// |BN_GENCB_set| to initialise a |BN_GENCB| structure.
 //
 // The callback receives the address of that |BN_GENCB| structure as its last
 // argument and the user is free to put an arbitrary pointer in |arg|. The other
@@ -648,9 +651,6 @@
 //
 // When other code needs to call a BN generation function it will often take a
 // BN_GENCB argument and may call the function with other argument values.
-#define BN_GENCB_GENERATED 0
-#define BN_GENCB_PRIME_TEST 1
-
 struct bn_gencb_st {
   void *arg;        // callback-specific data
   int (*callback)(int event, int n, struct bn_gencb_st *);
@@ -987,7 +987,7 @@
 #if !defined(BORINGSSL_NO_CXX)
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(BIGNUM, BN_free)
 BORINGSSL_MAKE_DELETER(BN_CTX, BN_CTX_free)
@@ -1005,7 +1005,7 @@
   BN_CTXScope &operator=(BN_CTXScope &) = delete;
 };
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 #endif
diff --git a/src/include/openssl/buf.h b/src/include/openssl/buf.h
index 3f961b8..10a555f 100644
--- a/src/include/openssl/buf.h
+++ b/src/include/openssl/buf.h
@@ -124,11 +124,11 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(BUF_MEM, BUF_MEM_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/bytestring.h b/src/include/openssl/bytestring.h
index 3057604..1400f2e 100644
--- a/src/include/openssl/bytestring.h
+++ b/src/include/openssl/bytestring.h
@@ -491,11 +491,11 @@
 #if !defined(BORINGSSL_NO_CXX)
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 using ScopedCBB = internal::StackAllocated<CBB, void, CBB_zero, CBB_cleanup>;
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 #endif
diff --git a/src/include/openssl/cipher.h b/src/include/openssl/cipher.h
index 727d7a7..5963413 100644
--- a/src/include/openssl/cipher.h
+++ b/src/include/openssl/cipher.h
@@ -438,7 +438,7 @@
 // EVP_CIPH_NO_PADDING disables padding in block ciphers.
 #define EVP_CIPH_NO_PADDING 0x800
 
-// EVP_CIPHER_CTX_ctrl commands.
+// The following are |EVP_CIPHER_CTX_ctrl| commands.
 #define EVP_CTRL_INIT 0x0
 #define EVP_CTRL_SET_KEY_LENGTH 0x1
 #define EVP_CTRL_GET_RC2_KEY_BITS 0x2
@@ -454,15 +454,12 @@
 #define EVP_CTRL_AEAD_SET_IV_FIXED 0x12
 #define EVP_CTRL_GCM_IV_GEN 0x13
 #define EVP_CTRL_AEAD_SET_MAC_KEY 0x17
-// Set the GCM invocation field, decrypt only
+// EVP_CTRL_GCM_SET_IV_INV sets the GCM invocation field, decrypt only
 #define EVP_CTRL_GCM_SET_IV_INV 0x18
 
-// GCM TLS constants
-// Length of fixed part of IV derived from PRF
+// The following constants are unused.
 #define EVP_GCM_TLS_FIXED_IV_LEN 4
-// Length of explicit part of IV part of TLS records
 #define EVP_GCM_TLS_EXPLICIT_IV_LEN 8
-// Length of tag for TLS
 #define EVP_GCM_TLS_TAG_LEN 16
 
 // The following are legacy aliases for AEAD |EVP_CIPHER_CTX_ctrl| values.
@@ -574,7 +571,7 @@
 #if !defined(BORINGSSL_NO_CXX)
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(EVP_CIPHER_CTX, EVP_CIPHER_CTX_free)
 
@@ -582,7 +579,7 @@
     internal::StackAllocated<EVP_CIPHER_CTX, int, EVP_CIPHER_CTX_init,
                              EVP_CIPHER_CTX_cleanup>;
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 #endif
diff --git a/src/include/openssl/cmac.h b/src/include/openssl/cmac.h
index 5e9f3d0..3e8cf92 100644
--- a/src/include/openssl/cmac.h
+++ b/src/include/openssl/cmac.h
@@ -78,11 +78,11 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(CMAC_CTX, CMAC_CTX_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/conf.h b/src/include/openssl/conf.h
index 4ffce37..7aa76e1 100644
--- a/src/include/openssl/conf.h
+++ b/src/include/openssl/conf.h
@@ -60,7 +60,6 @@
 #include <openssl/base.h>
 
 #include <openssl/stack.h>
-#include <openssl/lhash.h>
 
 #if defined(__cplusplus)
 extern "C" {
@@ -85,10 +84,6 @@
   char *value;
 };
 
-struct conf_st {
-  LHASH_OF(CONF_VALUE) *data;
-};
-
 DEFINE_STACK_OF(CONF_VALUE)
 
 
@@ -162,11 +157,11 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(CONF, NCONF_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/cpu.h b/src/include/openssl/cpu.h
index bb847f9..b2759fe 100644
--- a/src/include/openssl/cpu.h
+++ b/src/include/openssl/cpu.h
@@ -96,7 +96,7 @@
 #if defined(BORINGSSL_FIPS)
 const uint32_t *OPENSSL_ia32cap_get(void);
 #else
-static inline const uint32_t *OPENSSL_ia32cap_get(void) {
+OPENSSL_INLINE const uint32_t *OPENSSL_ia32cap_get(void) {
   return OPENSSL_ia32cap_P;
 }
 #endif
@@ -119,7 +119,7 @@
 
 // CRYPTO_is_NEON_capable returns true if the current CPU has a NEON unit. If
 // this is known statically then it returns one immediately.
-static inline int CRYPTO_is_NEON_capable(void) {
+OPENSSL_INLINE int CRYPTO_is_NEON_capable(void) {
   // Only statically skip the runtime lookup on aarch64. On arm, one CPU is
   // known to have a broken NEON unit which is known to fail with on some
   // hand-written NEON assembly. For now, continue to apply the workaround even
@@ -152,7 +152,7 @@
 
 #else
 
-static inline int CRYPTO_is_NEON_capable(void) {
+OPENSSL_INLINE int CRYPTO_is_NEON_capable(void) {
 #if defined(OPENSSL_STATIC_ARMCAP_NEON) || defined(__ARM_NEON__)
   return 1;
 #else
@@ -160,7 +160,7 @@
 #endif
 }
 
-static inline int CRYPTO_is_ARMv8_AES_capable(void) {
+OPENSSL_INLINE int CRYPTO_is_ARMv8_AES_capable(void) {
 #if defined(OPENSSL_STATIC_ARMCAP_AES) || defined(__ARM_FEATURE_CRYPTO)
   return 1;
 #else
@@ -168,7 +168,7 @@
 #endif
 }
 
-static inline int CRYPTO_is_ARMv8_PMULL_capable(void) {
+OPENSSL_INLINE int CRYPTO_is_ARMv8_PMULL_capable(void) {
 #if defined(OPENSSL_STATIC_ARMCAP_PMULL) || defined(__ARM_FEATURE_CRYPTO)
   return 1;
 #else
diff --git a/src/include/openssl/curve25519.h b/src/include/openssl/curve25519.h
index 9c841b6..a455389 100644
--- a/src/include/openssl/curve25519.h
+++ b/src/include/openssl/curve25519.h
@@ -79,7 +79,7 @@
 
 // ED25519_sign sets |out_sig| to be a signature of |message_len| bytes from
 // |message| using |private_key|. It returns one on success or zero on
-// error.
+// allocation failure.
 OPENSSL_EXPORT int ED25519_sign(uint8_t out_sig[64], const uint8_t *message,
                                 size_t message_len,
                                 const uint8_t private_key[64]);
@@ -188,11 +188,11 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(SPAKE2_CTX, SPAKE2_CTX_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/dh.h b/src/include/openssl/dh.h
index ae24c25..7e10303 100644
--- a/src/include/openssl/dh.h
+++ b/src/include/openssl/dh.h
@@ -278,11 +278,12 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(DH, DH_free)
+BORINGSSL_MAKE_UP_REF(DH, DH_up_ref)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/digest.h b/src/include/openssl/digest.h
index 4a2b710..1a1ca29 100644
--- a/src/include/openssl/digest.h
+++ b/src/include/openssl/digest.h
@@ -295,7 +295,7 @@
 #if !defined(BORINGSSL_NO_CXX)
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(EVP_MD_CTX, EVP_MD_CTX_free)
 
@@ -303,7 +303,7 @@
     internal::StackAllocated<EVP_MD_CTX, int, EVP_MD_CTX_init,
                              EVP_MD_CTX_cleanup>;
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 #endif
diff --git a/src/include/openssl/dsa.h b/src/include/openssl/dsa.h
index a5fa767..bed93c5 100644
--- a/src/include/openssl/dsa.h
+++ b/src/include/openssl/dsa.h
@@ -417,12 +417,13 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(DSA, DSA_free)
+BORINGSSL_MAKE_UP_REF(DSA, DSA_up_ref)
 BORINGSSL_MAKE_DELETER(DSA_SIG, DSA_SIG_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/ec.h b/src/include/openssl/ec.h
index dbb72ab..41a9c34 100644
--- a/src/include/openssl/ec.h
+++ b/src/include/openssl/ec.h
@@ -357,12 +357,12 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(EC_POINT, EC_POINT_free)
 BORINGSSL_MAKE_DELETER(EC_GROUP, EC_GROUP_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/ec_key.h b/src/include/openssl/ec_key.h
index a94116c..9bc7887 100644
--- a/src/include/openssl/ec_key.h
+++ b/src/include/openssl/ec_key.h
@@ -84,6 +84,12 @@
 
 
 // EC key objects.
+//
+// An |EC_KEY| object represents a public or private EC key. A given object may
+// be used concurrently on multiple threads by non-mutating functions, provided
+// no other thread is concurrently calling a mutating function. Unless otherwise
+// documented, functions which take a |const| pointer are non-mutating and
+// functions which take a non-|const| pointer are mutating.
 
 // EC_KEY_new returns a fresh |EC_KEY| object or NULL on error.
 OPENSSL_EXPORT EC_KEY *EC_KEY_new(void);
@@ -102,7 +108,8 @@
 // EC_KEY_dup returns a fresh copy of |src| or NULL on error.
 OPENSSL_EXPORT EC_KEY *EC_KEY_dup(const EC_KEY *src);
 
-// EC_KEY_up_ref increases the reference count of |key| and returns one.
+// EC_KEY_up_ref increases the reference count of |key| and returns one. It does
+// not mutate |key| for thread-safety purposes and may be used concurrently.
 OPENSSL_EXPORT int EC_KEY_up_ref(EC_KEY *key);
 
 // EC_KEY_is_opaque returns one if |key| is opaque and doesn't expose its key
@@ -329,11 +336,12 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(EC_KEY, EC_KEY_free)
+BORINGSSL_MAKE_UP_REF(EC_KEY, EC_KEY_up_ref)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/ecdsa.h b/src/include/openssl/ecdsa.h
index ff326ab..d4d353e 100644
--- a/src/include/openssl/ecdsa.h
+++ b/src/include/openssl/ecdsa.h
@@ -179,11 +179,11 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(ECDSA_SIG, ECDSA_SIG_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/engine.h b/src/include/openssl/engine.h
index 595e53c..9d45952 100644
--- a/src/include/openssl/engine.h
+++ b/src/include/openssl/engine.h
@@ -94,11 +94,11 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(ENGINE, ENGINE_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/evp.h b/src/include/openssl/evp.h
index 3719d7f..1d7192d 100644
--- a/src/include/openssl/evp.h
+++ b/src/include/openssl/evp.h
@@ -80,6 +80,12 @@
 
 
 // Public key objects.
+//
+// An |EVP_PKEY| object represents a public or private key. A given object may
+// be used concurrently on multiple threads by non-mutating functions, provided
+// no other thread is concurrently calling a mutating function. Unless otherwise
+// documented, functions which take a |const| pointer are non-mutating and
+// functions which take a non-|const| pointer are mutating.
 
 // EVP_PKEY_new creates a new, empty public-key object and returns it or NULL
 // on allocation failure.
@@ -89,7 +95,9 @@
 // itself.
 OPENSSL_EXPORT void EVP_PKEY_free(EVP_PKEY *pkey);
 
-// EVP_PKEY_up_ref increments the reference count of |pkey| and returns one.
+// EVP_PKEY_up_ref increments the reference count of |pkey| and returns one. It
+// does not mutate |pkey| for thread-safety purposes and may be used
+// concurrently.
 OPENSSL_EXPORT int EVP_PKEY_up_ref(EVP_PKEY *pkey);
 
 // EVP_PKEY_is_opaque returns one if |pkey| is opaque. Opaque keys are backed by
@@ -121,7 +129,7 @@
 // EVP_PKEY_bits returns the "size", in bits, of |pkey|. For an RSA key, this
 // returns the bit length of the modulus. For an EC key, this returns the bit
 // length of the group order.
-OPENSSL_EXPORT int EVP_PKEY_bits(EVP_PKEY *pkey);
+OPENSSL_EXPORT int EVP_PKEY_bits(const EVP_PKEY *pkey);
 
 // EVP_PKEY_id returns the type of |pkey|, which is one of the |EVP_PKEY_*|
 // values.
@@ -141,21 +149,26 @@
 // zero if |key| is NULL. The |get1| functions return a fresh reference to the
 // underlying object or NULL if |pkey| is not of the correct type. The |get0|
 // functions behave the same but return a non-owning pointer.
+//
+// The |get0| and |get1| functions take |const| pointers and are thus
+// non-mutating for thread-safety purposes, but mutating functions on the
+// returned lower-level objects are considered to also mutate the |EVP_PKEY| and
+// may not be called concurrently with other operations on the |EVP_PKEY|.
 
 OPENSSL_EXPORT int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key);
 OPENSSL_EXPORT int EVP_PKEY_assign_RSA(EVP_PKEY *pkey, RSA *key);
-OPENSSL_EXPORT RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey);
-OPENSSL_EXPORT RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey);
+OPENSSL_EXPORT RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey);
+OPENSSL_EXPORT RSA *EVP_PKEY_get1_RSA(const EVP_PKEY *pkey);
 
 OPENSSL_EXPORT int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key);
 OPENSSL_EXPORT int EVP_PKEY_assign_DSA(EVP_PKEY *pkey, DSA *key);
-OPENSSL_EXPORT DSA *EVP_PKEY_get0_DSA(EVP_PKEY *pkey);
-OPENSSL_EXPORT DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey);
+OPENSSL_EXPORT DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey);
+OPENSSL_EXPORT DSA *EVP_PKEY_get1_DSA(const EVP_PKEY *pkey);
 
 OPENSSL_EXPORT int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, EC_KEY *key);
 OPENSSL_EXPORT int EVP_PKEY_assign_EC_KEY(EVP_PKEY *pkey, EC_KEY *key);
-OPENSSL_EXPORT EC_KEY *EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey);
-OPENSSL_EXPORT EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey);
+OPENSSL_EXPORT EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey);
+OPENSSL_EXPORT EC_KEY *EVP_PKEY_get1_EC_KEY(const EVP_PKEY *pkey);
 
 // EVP_PKEY_new_ed25519_public returns a newly allocated |EVP_PKEY| wrapping an
 // Ed25519 public key, or NULL on allocation error.
@@ -240,6 +253,9 @@
 // Ed25519, |type| should be NULL. The |EVP_MD_CTX| itself is unused but is
 // present so the API is uniform. See |EVP_DigestSign|.
 //
+// This function does not mutate |pkey| for thread-safety purposes and may be
+// used concurrently with other non-mutating functions on |pkey|.
+//
 // It returns one on success, or zero on error.
 OPENSSL_EXPORT int EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
                                       const EVP_MD *type, ENGINE *e,
@@ -293,6 +309,9 @@
 // Ed25519, |type| should be NULL. The |EVP_MD_CTX| itself is unused but is
 // present so the API is uniform. See |EVP_DigestVerify|.
 //
+// This function does not mutate |pkey| for thread-safety purposes and may be
+// used concurrently with other non-mutating functions on |pkey|.
+//
 // It returns one on success, or zero on error.
 OPENSSL_EXPORT int EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
                                         const EVP_MD *type, ENGINE *e,
@@ -353,7 +372,9 @@
 // It returns one on success and zero otherwise.
 //
 // It does not modify |ctx|, thus it's possible to continue to use |ctx| in
-// order to sign a longer message.
+// order to sign a longer message. It also does not mutate |pkey| for
+// thread-safety purposes and may be used concurrently with other non-mutating
+// functions on |pkey|.
 OPENSSL_EXPORT int EVP_SignFinal(const EVP_MD_CTX *ctx, uint8_t *sig,
                                  unsigned int *out_sig_len, EVP_PKEY *pkey);
 
@@ -386,7 +407,9 @@
 // It returns one on success and zero otherwise.
 //
 // It does not modify |ctx|, thus it's possible to continue to use |ctx| in
-// order to sign a longer message.
+// order to verify a longer message. It also does not mutate |pkey| for
+// thread-safety purposes and may be used concurrently with other non-mutating
+// functions on |pkey|.
 OPENSSL_EXPORT int EVP_VerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig,
                                    size_t sig_len, EVP_PKEY *pkey);
 
@@ -779,7 +802,7 @@
 // EC keys are serialized as an EC point per SEC 1.
 //
 // Use |RSA_marshal_public_key| or |EC_POINT_point2cbb| instead.
-OPENSSL_EXPORT int i2d_PublicKey(EVP_PKEY *key, uint8_t **outp);
+OPENSSL_EXPORT int i2d_PublicKey(const EVP_PKEY *key, uint8_t **outp);
 
 // d2i_PrivateKey parses an ASN.1, DER-encoded, private key from |len| bytes at
 // |*inp|. If |out| is not NULL then, on exit, a pointer to the result is in
@@ -804,10 +827,10 @@
                                             long len);
 
 // EVP_PKEY_get0_DH returns NULL.
-OPENSSL_EXPORT DH *EVP_PKEY_get0_DH(EVP_PKEY *pkey);
+OPENSSL_EXPORT DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey);
 
 // EVP_PKEY_get1_DH returns NULL.
-OPENSSL_EXPORT DH *EVP_PKEY_get1_DH(EVP_PKEY *pkey);
+OPENSSL_EXPORT DH *EVP_PKEY_get1_DH(const EVP_PKEY *pkey);
 
 
 // Preprocessor compatibility section (hidden).
@@ -816,8 +839,12 @@
 // constants to 'ctrl' functions. To avoid breaking #ifdefs in consumers, this
 // section defines a number of legacy macros.
 
+// |BORINGSSL_PREFIX| already makes each of these symbols into macros, so there
+// is no need to define conflicting macros.
+#if !defined(BORINGSSL_PREFIX)
 #define EVP_PKEY_CTX_set_rsa_oaep_md EVP_PKEY_CTX_set_rsa_oaep_md
 #define EVP_PKEY_CTX_set0_rsa_oaep_label EVP_PKEY_CTX_set0_rsa_oaep_label
+#endif
 
 
 // Private structures.
@@ -847,13 +874,13 @@
 }  // extern C
 
 extern "C++" {
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(EVP_PKEY, EVP_PKEY_free)
 BORINGSSL_MAKE_UP_REF(EVP_PKEY, EVP_PKEY_up_ref)
 BORINGSSL_MAKE_DELETER(EVP_PKEY_CTX, EVP_PKEY_CTX_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/hmac.h b/src/include/openssl/hmac.h
index 977dea6..b5d1e42 100644
--- a/src/include/openssl/hmac.h
+++ b/src/include/openssl/hmac.h
@@ -169,14 +169,14 @@
 #if !defined(BORINGSSL_NO_CXX)
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(HMAC_CTX, HMAC_CTX_free)
 
 using ScopedHMAC_CTX =
     internal::StackAllocated<HMAC_CTX, void, HMAC_CTX_init, HMAC_CTX_cleanup>;
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 #endif
diff --git a/src/include/openssl/mem.h b/src/include/openssl/mem.h
index 7d7087e..9f9c00d 100644
--- a/src/include/openssl/mem.h
+++ b/src/include/openssl/mem.h
@@ -142,12 +142,12 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(char, OPENSSL_free)
 BORINGSSL_MAKE_DELETER(uint8_t, OPENSSL_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/pem.h b/src/include/openssl/pem.h
index a43ca0d..9c0ff93 100644
--- a/src/include/openssl/pem.h
+++ b/src/include/openssl/pem.h
@@ -123,73 +123,109 @@
 
 #else
 
-#define IMPLEMENT_PEM_read_fp(name, type, str, asn1) \
-OPENSSL_EXPORT type *PEM_read_##name(FILE *fp, type **x, pem_password_cb *cb, void *u)\
-{ \
-return (type *)PEM_ASN1_read((d2i_of_void *)d2i_##asn1, str,fp,(void **)x,cb,u); \
-} 
+#define IMPLEMENT_PEM_read_fp(name, type, str, asn1)                         \
+  static void *pem_read_##name##_d2i(void **x, const unsigned char **inp,    \
+                                     long len) {                             \
+    return d2i_##asn1((type **)x, inp, len);                                 \
+  }                                                                          \
+  OPENSSL_EXPORT type *PEM_read_##name(FILE *fp, type **x,                   \
+                                       pem_password_cb *cb, void *u) {       \
+    return (type *)PEM_ASN1_read(pem_read_##name##_d2i, str, fp, (void **)x, \
+                                 cb, u);                                     \
+  }
 
-#define IMPLEMENT_PEM_write_fp(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_##name(FILE *fp, type *x) \
-{ \
-return PEM_ASN1_write((i2d_of_void *)i2d_##asn1,str,fp,x,NULL,NULL,0,NULL,NULL); \
-}
+#define IMPLEMENT_PEM_write_fp(name, type, str, asn1)                        \
+  static int pem_write_##name##_i2d(const void *x, unsigned char **outp) {   \
+    return i2d_##asn1((type *)x, outp);                                      \
+  }                                                                          \
+  OPENSSL_EXPORT int PEM_write_##name(FILE *fp, type *x) {                   \
+    return PEM_ASN1_write(pem_write_##name##_i2d, str, fp, x, NULL, NULL, 0, \
+                          NULL, NULL);                                       \
+  }
 
-#define IMPLEMENT_PEM_write_fp_const(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_##name(FILE *fp, const type *x) \
-{ \
-return PEM_ASN1_write((i2d_of_void *)i2d_##asn1,str,fp,(void *)x,NULL,NULL,0,NULL,NULL); \
-}
+#define IMPLEMENT_PEM_write_fp_const(name, type, str, asn1)                 \
+  static int pem_write_##name##_i2d(const void *x, unsigned char **outp) {  \
+    return i2d_##asn1((const type *)x, outp);                               \
+  }                                                                         \
+  OPENSSL_EXPORT int PEM_write_##name(FILE *fp, const type *x) {            \
+    return PEM_ASN1_write(pem_write_##name##_i2d, str, fp, (void *)x, NULL, \
+                          NULL, 0, NULL, NULL);                             \
+  }
 
-#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_##name(FILE *fp, type *x, const EVP_CIPHER *enc, \
-	     unsigned char *kstr, int klen, pem_password_cb *cb, \
-		  void *u) \
-	{ \
-	return PEM_ASN1_write((i2d_of_void *)i2d_##asn1,str,fp,x,enc,kstr,klen,cb,u); \
-	}
+#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1)                       \
+  static int pem_write_##name##_i2d(const void *x, unsigned char **outp) {     \
+    return i2d_##asn1((type *)x, outp);                                        \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_##name(                                         \
+      FILE *fp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen, \
+      pem_password_cb *cb, void *u) {                                          \
+    return PEM_ASN1_write(pem_write_##name##_i2d, str, fp, x, enc, kstr, klen, \
+                          cb, u);                                              \
+  }
 
-#define IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_##name(FILE *fp, type *x, const EVP_CIPHER *enc, \
-	     unsigned char *kstr, int klen, pem_password_cb *cb, \
-		  void *u) \
-	{ \
-	return PEM_ASN1_write((i2d_of_void *)i2d_##asn1,str,fp,x,enc,kstr,klen,cb,u); \
-	}
+#define IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1)                 \
+  static int pem_write_##name##_i2d(const void *x, unsigned char **outp) {     \
+    return i2d_##asn1((const type *)x, outp);                                  \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_##name(                                         \
+      FILE *fp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen, \
+      pem_password_cb *cb, void *u) {                                          \
+    return PEM_ASN1_write(pem_write_##name##_i2d, str, fp, x, enc, kstr, klen, \
+                          cb, u);                                              \
+  }
 
 #endif
 
-#define IMPLEMENT_PEM_read_bio(name, type, str, asn1) \
-OPENSSL_EXPORT type *PEM_read_bio_##name(BIO *bp, type **x, pem_password_cb *cb, void *u)\
-{ \
-return (type *)PEM_ASN1_read_bio((d2i_of_void *)d2i_##asn1, str,bp,(void **)x,cb,u); \
-}
+#define IMPLEMENT_PEM_read_bio(name, type, str, asn1)                         \
+  static void *pem_read_bio_##name##_d2i(void **x, const unsigned char **inp, \
+                                         long len) {                          \
+    return d2i_##asn1((type **)x, inp, len);                                  \
+  }                                                                           \
+  OPENSSL_EXPORT type *PEM_read_bio_##name(BIO *bp, type **x,                 \
+                                           pem_password_cb *cb, void *u) {    \
+    return (type *)PEM_ASN1_read_bio(pem_read_bio_##name##_d2i, str, bp,      \
+                                     (void **)x, cb, u);                      \
+  }
 
-#define IMPLEMENT_PEM_write_bio(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, type *x) \
-{ \
-return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1,str,bp,x,NULL,NULL,0,NULL,NULL); \
-}
+#define IMPLEMENT_PEM_write_bio(name, type, str, asn1)                         \
+  static int pem_write_bio_##name##_i2d(const void *x, unsigned char **outp) { \
+    return i2d_##asn1((type *)x, outp);                                        \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, type *x) {                  \
+    return PEM_ASN1_write_bio(pem_write_bio_##name##_i2d, str, bp, x, NULL,    \
+                              NULL, 0, NULL, NULL);                            \
+  }
 
-#define IMPLEMENT_PEM_write_bio_const(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, const type *x) \
-{ \
-return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1,str,bp,(void *)x,NULL,NULL,0,NULL,NULL); \
-}
+#define IMPLEMENT_PEM_write_bio_const(name, type, str, asn1)                   \
+  static int pem_write_bio_##name##_i2d(const void *x, unsigned char **outp) { \
+    return i2d_##asn1((const type *)x, outp);                                  \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, const type *x) {            \
+    return PEM_ASN1_write_bio(pem_write_bio_##name##_i2d, str, bp, (void *)x,  \
+                              NULL, NULL, 0, NULL, NULL);                      \
+  }
 
-#define IMPLEMENT_PEM_write_cb_bio(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, type *x, const EVP_CIPHER *enc, \
-	     unsigned char *kstr, int klen, pem_password_cb *cb, void *u) \
-	{ \
-	return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1,str,bp,x,enc,kstr,klen,cb,u); \
-	}
+#define IMPLEMENT_PEM_write_cb_bio(name, type, str, asn1)                      \
+  static int pem_write_bio_##name##_i2d(const void *x, unsigned char **outp) { \
+    return i2d_##asn1((type *)x, outp);                                        \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_bio_##name(                                     \
+      BIO *bp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen,  \
+      pem_password_cb *cb, void *u) {                                          \
+    return PEM_ASN1_write_bio(pem_write_bio_##name##_i2d, str, bp, x, enc,     \
+                              kstr, klen, cb, u);                              \
+  }
 
-#define IMPLEMENT_PEM_write_cb_bio_const(name, type, str, asn1) \
-OPENSSL_EXPORT int PEM_write_bio_##name(BIO *bp, type *x, const EVP_CIPHER *enc, \
-	     unsigned char *kstr, int klen, pem_password_cb *cb, void *u) \
-	{ \
-	return PEM_ASN1_write_bio((i2d_of_void *)i2d_##asn1,str,bp,(void *)x,enc,kstr,klen,cb,u); \
-	}
+#define IMPLEMENT_PEM_write_cb_bio_const(name, type, str, asn1)                \
+  static int pem_write_bio_##name##_i2d(const void *x, unsigned char **outp) { \
+    return i2d_##asn1((const type *)x, outp);                                  \
+  }                                                                            \
+  OPENSSL_EXPORT int PEM_write_bio_##name(                                     \
+      BIO *bp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen,  \
+      pem_password_cb *cb, void *u) {                                          \
+    return PEM_ASN1_write_bio(pem_write_bio_##name##_i2d, str, bp, (void *)x,  \
+                              enc, kstr, klen, cb, u);                         \
+  }
 
 #define IMPLEMENT_PEM_write(name, type, str, asn1) \
 	IMPLEMENT_PEM_write_bio(name, type, str, asn1) \
diff --git a/src/include/openssl/pkcs7.h b/src/include/openssl/pkcs7.h
index 52b649c..cb6155f 100644
--- a/src/include/openssl/pkcs7.h
+++ b/src/include/openssl/pkcs7.h
@@ -199,11 +199,11 @@
 }  // extern C
 
 extern "C++" {
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(PKCS7, PKCS7_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 }  // extern C++
 #endif
 
diff --git a/src/include/openssl/pkcs8.h b/src/include/openssl/pkcs8.h
index 9a66dd0..ee48f19 100644
--- a/src/include/openssl/pkcs8.h
+++ b/src/include/openssl/pkcs8.h
@@ -215,12 +215,12 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(PKCS12, PKCS12_free)
 BORINGSSL_MAKE_DELETER(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/pool.h b/src/include/openssl/pool.h
index 1259f4a..0e4bdd5 100644
--- a/src/include/openssl/pool.h
+++ b/src/include/openssl/pool.h
@@ -87,13 +87,13 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(CRYPTO_BUFFER_POOL, CRYPTO_BUFFER_POOL_free)
 BORINGSSL_MAKE_DELETER(CRYPTO_BUFFER, CRYPTO_BUFFER_free)
 BORINGSSL_MAKE_UP_REF(CRYPTO_BUFFER, CRYPTO_BUFFER_up_ref)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/rsa.h b/src/include/openssl/rsa.h
index 7ed5dce..dcca9e3 100644
--- a/src/include/openssl/rsa.h
+++ b/src/include/openssl/rsa.h
@@ -72,8 +72,14 @@
 
 
 // Allocation and destruction.
+//
+// An |RSA| object represents a public or private RSA key. A given object may be
+// used concurrently on multiple threads by non-mutating functions, provided no
+// other thread is concurrently calling a mutating function. Unless otherwise
+// documented, functions which take a |const| pointer are non-mutating and
+// functions which take a non-|const| pointer are mutating.
 
-// RSA_new returns a new, empty RSA object or NULL on error.
+// RSA_new returns a new, empty |RSA| object or NULL on error.
 OPENSSL_EXPORT RSA *RSA_new(void);
 
 // RSA_new_method acts the same as |RSA_new| but takes an explicit |ENGINE|.
@@ -83,7 +89,8 @@
 // reference count drops to zero.
 OPENSSL_EXPORT void RSA_free(RSA *rsa);
 
-// RSA_up_ref increments the reference count of |rsa| and returns one.
+// RSA_up_ref increments the reference count of |rsa| and returns one. It does
+// not mutate |rsa| for thread-safety purposes and may be used concurrently.
 OPENSSL_EXPORT int RSA_up_ref(RSA *rsa);
 
 
@@ -164,12 +171,23 @@
 
 
 // Encryption / Decryption
+//
+// These functions are considered non-mutating for thread-safety purposes and
+// may be used concurrently.
 
-// Padding types for encryption.
+// RSA_PKCS1_PADDING denotes PKCS#1 v1.5 padding. When used with encryption,
+// this is RSAES-PKCS1-v1_5. When used with signing, this is RSASSA-PKCS1-v1_5.
 #define RSA_PKCS1_PADDING 1
+
+// RSA_NO_PADDING denotes a raw RSA operation.
 #define RSA_NO_PADDING 3
+
+// RSA_PKCS1_OAEP_PADDING denotes the RSAES-OAEP encryption scheme.
 #define RSA_PKCS1_OAEP_PADDING 4
-// RSA_PKCS1_PSS_PADDING can only be used via the EVP interface.
+
+// RSA_PKCS1_PSS_PADDING denotes the RSASSA-PSS signature scheme. This value may
+// not be passed into |RSA_sign_raw|, only |EVP_PKEY_CTX_set_rsa_padding|. See
+// also |RSA_sign_pss_mgf1| and |RSA_verify_pss_mgf1|.
 #define RSA_PKCS1_PSS_PADDING 6
 
 // RSA_encrypt encrypts |in_len| bytes from |in| to the public key from |rsa|
@@ -231,6 +249,9 @@
 
 
 // Signing / Verification
+//
+// These functions are considered non-mutating for thread-safety purposes and
+// may be used concurrently.
 
 // RSA_sign signs |in_len| bytes of digest from |in| with |rsa| using
 // RSASSA-PKCS1-v1_5. It writes, at most, |RSA_size(rsa)| bytes to |out|. On
@@ -272,7 +293,8 @@
 //
 // The |padding| argument must be one of the |RSA_*_PADDING| values. If in
 // doubt, |RSA_PKCS1_PADDING| is the most common but |RSA_PKCS1_PSS_PADDING|
-// (via the |EVP_PKEY| interface) is preferred for new protocols.
+// (via |RSA_sign_pss_mgf1| or the |EVP_PKEY| interface) is preferred for new
+// protocols.
 OPENSSL_EXPORT int RSA_sign_raw(RSA *rsa, size_t *out_len, uint8_t *out,
                                 size_t max_out, const uint8_t *in,
                                 size_t in_len, int padding);
@@ -317,7 +339,8 @@
 //
 // The |padding| argument must be one of the |RSA_*_PADDING| values. If in
 // doubt, |RSA_PKCS1_PADDING| is the most common but |RSA_PKCS1_PSS_PADDING|
-// (via the |EVP_PKEY| interface) is preferred for new protocols.
+// (via |RSA_verify_pss_mgf1| or the |EVP_PKEY| interface) is preferred for new
+// protocols.
 OPENSSL_EXPORT int RSA_verify_raw(RSA *rsa, size_t *out_len, uint8_t *out,
                                   size_t max_out, const uint8_t *in,
                                   size_t in_len, int padding);
@@ -372,8 +395,9 @@
 // returns zero then a more detailed error is available on the error queue.
 OPENSSL_EXPORT int RSA_check_key(const RSA *rsa);
 
-// RSA_check_fips performs public key validity tests on |key|. It returns one
-// if they pass and zero otherwise. Opaque keys always fail.
+// RSA_check_fips performs public key validity tests on |key|. It returns one if
+// they pass and zero otherwise. Opaque keys always fail. This function does not
+// mutate |rsa| for thread-safety purposes and may be used concurrently.
 OPENSSL_EXPORT int RSA_check_fips(RSA *key);
 
 // RSA_verify_PKCS1_PSS_mgf1 verifies that |EM| is a correct PSS padding of
@@ -390,7 +414,8 @@
 //
 // This function implements only the low-level padding logic. Use
 // |RSA_verify_pss_mgf1| instead.
-OPENSSL_EXPORT int RSA_verify_PKCS1_PSS_mgf1(RSA *rsa, const uint8_t *mHash,
+OPENSSL_EXPORT int RSA_verify_PKCS1_PSS_mgf1(const RSA *rsa,
+                                             const uint8_t *mHash,
                                              const EVP_MD *Hash,
                                              const EVP_MD *mgf1Hash,
                                              const uint8_t *EM, int sLen);
@@ -407,7 +432,7 @@
 //
 // This function implements only the low-level padding logic. Use
 // |RSA_sign_pss_mgf1| instead.
-OPENSSL_EXPORT int RSA_padding_add_PKCS1_PSS_mgf1(RSA *rsa, uint8_t *EM,
+OPENSSL_EXPORT int RSA_padding_add_PKCS1_PSS_mgf1(const RSA *rsa, uint8_t *EM,
                                                   const uint8_t *mHash,
                                                   const EVP_MD *Hash,
                                                   const EVP_MD *mgf1Hash,
@@ -567,7 +592,7 @@
 //
 // This function implements only the low-level padding logic. Use
 // |RSA_sign_pss_mgf1| instead.
-OPENSSL_EXPORT int RSA_padding_add_PKCS1_PSS(RSA *rsa, uint8_t *EM,
+OPENSSL_EXPORT int RSA_padding_add_PKCS1_PSS(const RSA *rsa, uint8_t *EM,
                                              const uint8_t *mHash,
                                              const EVP_MD *Hash, int sLen);
 
@@ -576,7 +601,7 @@
 //
 // This function implements only the low-level padding logic. Use
 // |RSA_verify_pss_mgf1| instead.
-OPENSSL_EXPORT int RSA_verify_PKCS1_PSS(RSA *rsa, const uint8_t *mHash,
+OPENSSL_EXPORT int RSA_verify_PKCS1_PSS(const RSA *rsa, const uint8_t *mHash,
                                         const EVP_MD *Hash, const uint8_t *EM,
                                         int sLen);
 
@@ -698,11 +723,12 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(RSA, RSA_free)
+BORINGSSL_MAKE_UP_REF(RSA, RSA_up_ref)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/sha.h b/src/include/openssl/sha.h
index 306dc80..2c0dcb7 100644
--- a/src/include/openssl/sha.h
+++ b/src/include/openssl/sha.h
@@ -211,11 +211,6 @@
 // |out|.
 OPENSSL_EXPORT uint8_t *SHA384(const uint8_t *data, size_t len, uint8_t *out);
 
-// SHA384_Transform is a low-level function that performs a single, SHA-384
-// block transformation using the state from |sha| and |SHA384_CBLOCK| bytes
-// from |block|.
-OPENSSL_EXPORT void SHA384_Transform(SHA512_CTX *sha, const uint8_t *block);
-
 
 // SHA-512.
 
diff --git a/src/include/openssl/span.h b/src/include/openssl/span.h
index 5ed96b7..298a722 100644
--- a/src/include/openssl/span.h
+++ b/src/include/openssl/span.h
@@ -25,7 +25,7 @@
 #include <cstdlib>
 #include <type_traits>
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 template <typename T>
 class Span;
@@ -190,7 +190,7 @@
   return MakeConstSpan(c.data(), c.size());
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index daa58b0..c0d44ce 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -3004,7 +3004,7 @@
 OPENSSL_EXPORT const char *SSL_get_psk_identity(const SSL *ssl);
 
 
-// QUIC Transport Parameters.
+// QUIC transport parameters.
 //
 // draft-ietf-quic-tls defines a new TLS extension quic_transport_parameters
 // used by QUIC for each endpoint to unilaterally declare its supported
@@ -3667,6 +3667,10 @@
 OPENSSL_EXPORT void SSL_CTX_set_ignore_tls13_downgrade(SSL_CTX *ctx,
                                                        int ignore);
 
+// SSL_set_ignore_tls13_downgrade configures whether |ssl| ignores the downgrade
+// signal in the server's random value.
+OPENSSL_EXPORT void SSL_set_ignore_tls13_downgrade(SSL *ssl, int ignore);
+
 // SSL_is_tls13_downgrade returns one if the TLS 1.3 anti-downgrade
 // mechanism would have aborted |ssl|'s handshake and zero otherwise.
 OPENSSL_EXPORT int SSL_is_tls13_downgrade(const SSL *ssl);
@@ -4314,6 +4318,7 @@
 //
 // These defines exist for node.js, with the hope that we can eliminate the
 // need for them over time.
+
 #define SSLerr(function, reason) \
   ERR_put_error(ERR_LIB_SSL, 0, reason, __FILE__, __LINE__)
 
@@ -4382,6 +4387,10 @@
 #define SSL_CTRL_SET_TMP_RSA doesnt_exist
 #define SSL_CTRL_SET_TMP_RSA_CB doesnt_exist
 
+// |BORINGSSL_PREFIX| already makes each of these symbols into macros, so there
+// is no need to define conflicting macros.
+#if !defined(BORINGSSL_PREFIX)
+
 #define DTLSv1_get_timeout DTLSv1_get_timeout
 #define DTLSv1_handle_timeout DTLSv1_handle_timeout
 #define SSL_CTX_add0_chain_cert SSL_CTX_add0_chain_cert
@@ -4451,6 +4460,8 @@
 #define SSL_set_tmp_rsa SSL_set_tmp_rsa
 #define SSL_total_renegotiations SSL_total_renegotiations
 
+#endif // !defined(BORINGSSL_PREFIX)
+
 
 #if defined(__cplusplus)
 }  // extern C
@@ -4459,7 +4470,7 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(SSL, SSL_free)
 BORINGSSL_MAKE_DELETER(SSL_CTX, SSL_CTX_free)
@@ -4571,7 +4582,7 @@
 OPENSSL_EXPORT bool SSL_serialize_handback(const SSL *ssl, CBB *out);
 OPENSSL_EXPORT bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback);
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  // extern C++
 
diff --git a/src/include/openssl/stack.h b/src/include/openssl/stack.h
index a1cca59..c1bf520 100644
--- a/src/include/openssl/stack.h
+++ b/src/include/openssl/stack.h
@@ -86,10 +86,23 @@
 // STACK_OF(FOO), the macros would be sk_FOO_new, sk_FOO_pop etc.
 
 
+// stack_free_func is a function that frees an element in a stack. Note its
+// actual type is void (*)(T *) for some T. Low-level |sk_*| functions will be
+// passed a type-specific wrapper to call it correctly.
+typedef void (*stack_free_func)(void *ptr);
+
+// stack_copy_func is a function that copies an element in a stack. Note its
+// actual type is T *(*)(T *) for some T. Low-level |sk_*| functions will be
+// passed a type-specific wrapper to call it correctly.
+typedef void *(*stack_copy_func)(void *ptr);
+
 // stack_cmp_func is a comparison function that returns a value < 0, 0 or > 0
 // if |*a| is less than, equal to or greater than |*b|, respectively.  Note the
 // extra indirection - the function is given a pointer to a pointer to the
 // element. This differs from the usual qsort/bsearch comparison function.
+//
+// Note its actual type is int (*)(const T **, const T **). Low-level |sk_*|
+// functions will be passed a type-specific wrapper to call it correctly.
 typedef int (*stack_cmp_func)(const void **a, const void **b);
 
 // stack_st contains an array of pointers. It is not designed to be used
@@ -140,12 +153,17 @@
 OPENSSL_EXPORT void *sk_set(_STACK *sk, size_t i, void *p);
 
 // sk_free frees the given stack and array of pointers, but does nothing to
-// free the individual elements. Also see |sk_pop_free|.
+// free the individual elements. Also see |sk_pop_free_ex|.
 OPENSSL_EXPORT void sk_free(_STACK *sk);
 
-// sk_pop_free calls |free_func| on each element in the stack and then frees
-// the stack itself.
-OPENSSL_EXPORT void sk_pop_free(_STACK *sk, void (*free_func)(void *));
+// sk_pop_free_ex calls |free_func| on each element in the stack and then frees
+// the stack itself. Note this corresponds to |sk_FOO_pop_free|. It is named
+// |sk_pop_free_ex| as a workaround for existing code calling an older version
+// of |sk_pop_free|.
+OPENSSL_EXPORT void sk_pop_free_ex(_STACK *sk,
+                                   void (*call_free_func)(stack_free_func,
+                                                          void *),
+                                   stack_free_func free_func);
 
 // sk_insert inserts |p| into the stack at index |where|, moving existing
 // elements if needed. It returns the length of the new stack, or zero on
@@ -160,7 +178,7 @@
 // sk_delete_ptr removes, at most, one instance of |p| from the stack based on
 // pointer equality. If an instance of |p| is found then |p| is returned,
 // otherwise it returns NULL.
-OPENSSL_EXPORT void *sk_delete_ptr(_STACK *sk, void *p);
+OPENSSL_EXPORT void *sk_delete_ptr(_STACK *sk, const void *p);
 
 // sk_find returns the first value in the stack equal to |p|. If a comparison
 // function has been set on the stack, equality is defined by it, otherwise
@@ -173,7 +191,9 @@
 // Note this differs from OpenSSL. The type signature is slightly different, and
 // OpenSSL's sk_find will implicitly sort |sk| if it has a comparison function
 // defined.
-OPENSSL_EXPORT int sk_find(const _STACK *sk, size_t *out_index, void *p);
+OPENSSL_EXPORT int sk_find(const _STACK *sk, size_t *out_index, const void *p,
+                           int (*call_cmp_func)(stack_cmp_func, const void **,
+                                                const void **));
 
 // sk_shift removes and returns the first element in the stack, or returns NULL
 // if the stack is empty.
@@ -207,9 +227,20 @@
 // sk_deep_copy performs a copy of |sk| and of each of the non-NULL elements in
 // |sk| by using |copy_func|. If an error occurs, |free_func| is used to free
 // any copies already made and NULL is returned.
-OPENSSL_EXPORT _STACK *sk_deep_copy(const _STACK *sk,
-                                    void *(*copy_func)(void *),
-                                    void (*free_func)(void *));
+OPENSSL_EXPORT _STACK *sk_deep_copy(
+    const _STACK *sk, void *(*call_copy_func)(stack_copy_func, void *),
+    stack_copy_func copy_func, void (*call_free_func)(stack_free_func, void *),
+    stack_free_func free_func);
+
+
+// Deprecated functions.
+
+// sk_pop_free behaves like |sk_pop_free_ex| but performs an invalid function
+// pointer cast. It exists because some existing callers called |sk_pop_free|
+// directly.
+//
+// TODO(davidben): Migrate callers to bssl::UniquePtr and remove this.
+OPENSSL_EXPORT void sk_pop_free(_STACK *sk, stack_free_func free_func);
 
 
 // Defining stack types.
@@ -219,17 +250,17 @@
 
 #if !defined(BORINGSSL_NO_CXX)
 extern "C++" {
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 namespace internal {
 template <typename T>
 struct StackTraits {};
 }
-}
+BSSL_NAMESPACE_END
 }
 
 #define BORINGSSL_DEFINE_STACK_TRAITS(name, type, is_const) \
   extern "C++" {                                            \
-  namespace bssl {                                          \
+  BSSL_NAMESPACE_BEGIN                                      \
   namespace internal {                                      \
   template <>                                               \
   struct StackTraits<STACK_OF(name)> {                      \
@@ -238,120 +269,133 @@
     static constexpr bool kIsConst = is_const;              \
   };                                                        \
   }                                                         \
-  }                                                         \
+  BSSL_NAMESPACE_END                                        \
   }
 
 #else
 #define BORINGSSL_DEFINE_STACK_TRAITS(name, type, is_const)
 #endif
 
-// Stack functions must be tagged unused to support file-local stack types.
-// Clang's -Wunused-function only allows unused static inline functions if they
-// are defined in a header.
-
 #define BORINGSSL_DEFINE_STACK_OF_IMPL(name, ptrtype, constptrtype)            \
   DECLARE_STACK_OF(name)                                                       \
                                                                                \
+  typedef void (*stack_##name##_free_func)(ptrtype);                           \
+  typedef ptrtype (*stack_##name##_copy_func)(ptrtype);                        \
   typedef int (*stack_##name##_cmp_func)(constptrtype *a, constptrtype *b);    \
                                                                                \
-  static inline OPENSSL_UNUSED STACK_OF(name) *                                \
+  OPENSSL_INLINE void sk_##name##_call_free_func(stack_free_func free_func,    \
+                                                 void *ptr) {                  \
+    ((stack_##name##_free_func)free_func)((ptrtype)ptr);                       \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE void *sk_##name##_call_copy_func(stack_copy_func copy_func,   \
+                                                  void *ptr) {                 \
+    return (void *)((stack_##name##_copy_func)copy_func)((ptrtype)ptr);        \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE int sk_##name##_call_cmp_func(                                \
+      stack_cmp_func cmp_func, const void **a, const void **b) {               \
+    constptrtype a_ptr = (constptrtype)*a;                                     \
+    constptrtype b_ptr = (constptrtype)*b;                                     \
+    return ((stack_##name##_cmp_func)cmp_func)(&a_ptr, &b_ptr);                \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE STACK_OF(name) *                                              \
       sk_##name##_new(stack_##name##_cmp_func comp) {                          \
     return (STACK_OF(name) *)sk_new((stack_cmp_func)comp);                     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED STACK_OF(name) *sk_##name##_new_null(void) {    \
+  OPENSSL_INLINE STACK_OF(name) *sk_##name##_new_null(void) {                  \
     return (STACK_OF(name) *)sk_new_null();                                    \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED size_t sk_##name##_num(                         \
-      const STACK_OF(name) *sk) {                                              \
+  OPENSSL_INLINE size_t sk_##name##_num(const STACK_OF(name) *sk) {            \
     return sk_num((const _STACK *)sk);                                         \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED void sk_##name##_zero(STACK_OF(name) *sk) {     \
+  OPENSSL_INLINE void sk_##name##_zero(STACK_OF(name) *sk) {                   \
     sk_zero((_STACK *)sk);                                                     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_value(                      \
-      const STACK_OF(name) *sk, size_t i) {                                    \
+  OPENSSL_INLINE ptrtype sk_##name##_value(const STACK_OF(name) *sk,           \
+                                           size_t i) {                         \
     return (ptrtype)sk_value((const _STACK *)sk, i);                           \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_set(STACK_OF(name) *sk,     \
-                                                       size_t i, ptrtype p) {  \
+  OPENSSL_INLINE ptrtype sk_##name##_set(STACK_OF(name) *sk, size_t i,         \
+                                         ptrtype p) {                          \
     return (ptrtype)sk_set((_STACK *)sk, i, (void *)p);                        \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED void sk_##name##_free(STACK_OF(name) *sk) {     \
+  OPENSSL_INLINE void sk_##name##_free(STACK_OF(name) * sk) {                  \
     sk_free((_STACK *)sk);                                                     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED void sk_##name##_pop_free(                      \
-      STACK_OF(name) *sk, void (*free_func)(ptrtype p)) {                      \
-    sk_pop_free((_STACK *)sk, (void (*)(void *))free_func);                    \
+  OPENSSL_INLINE void sk_##name##_pop_free(                                    \
+      STACK_OF(name) * sk, stack_##name##_free_func free_func) {               \
+    sk_pop_free_ex((_STACK *)sk, sk_##name##_call_free_func,                   \
+                   (stack_free_func)free_func);                                \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED size_t sk_##name##_insert(                      \
-      STACK_OF(name) *sk, ptrtype p, size_t where) {                           \
+  OPENSSL_INLINE size_t sk_##name##_insert(STACK_OF(name) *sk, ptrtype p,      \
+                                           size_t where) {                     \
     return sk_insert((_STACK *)sk, (void *)p, where);                          \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_delete(STACK_OF(name) *sk,  \
-                                                          size_t where) {      \
+  OPENSSL_INLINE ptrtype sk_##name##_delete(STACK_OF(name) *sk,                \
+                                            size_t where) {                    \
     return (ptrtype)sk_delete((_STACK *)sk, where);                            \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_delete_ptr(                 \
-      STACK_OF(name) *sk, ptrtype p) {                                         \
-    return (ptrtype)sk_delete_ptr((_STACK *)sk, (void *)p);                    \
+  OPENSSL_INLINE ptrtype sk_##name##_delete_ptr(STACK_OF(name) *sk,            \
+                                                constptrtype p) {              \
+    return (ptrtype)sk_delete_ptr((_STACK *)sk, (const void *)p);              \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED int sk_##name##_find(                           \
-      const STACK_OF(name) *sk, size_t *out_index, ptrtype p) {                \
-    return sk_find((const _STACK *)sk, out_index, (void *)p);                  \
+  OPENSSL_INLINE int sk_##name##_find(const STACK_OF(name) *sk,                \
+                                      size_t * out_index, constptrtype p) {    \
+    return sk_find((const _STACK *)sk, out_index, (const void *)p,             \
+                   sk_##name##_call_cmp_func);                                 \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_shift(STACK_OF(name) *sk) { \
+  OPENSSL_INLINE ptrtype sk_##name##_shift(STACK_OF(name) *sk) {               \
     return (ptrtype)sk_shift((_STACK *)sk);                                    \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED size_t sk_##name##_push(STACK_OF(name) *sk,     \
-                                                       ptrtype p) {            \
+  OPENSSL_INLINE size_t sk_##name##_push(STACK_OF(name) *sk, ptrtype p) {      \
     return sk_push((_STACK *)sk, (void *)p);                                   \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED ptrtype sk_##name##_pop(STACK_OF(name) *sk) {   \
+  OPENSSL_INLINE ptrtype sk_##name##_pop(STACK_OF(name) *sk) {                 \
     return (ptrtype)sk_pop((_STACK *)sk);                                      \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED STACK_OF(name) *                                \
-      sk_##name##_dup(const STACK_OF(name) *sk) {                              \
+  OPENSSL_INLINE STACK_OF(name) * sk_##name##_dup(const STACK_OF(name) *sk) {  \
     return (STACK_OF(name) *)sk_dup((const _STACK *)sk);                       \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED void sk_##name##_sort(STACK_OF(name) *sk) {     \
+  OPENSSL_INLINE void sk_##name##_sort(STACK_OF(name) *sk) {                   \
     sk_sort((_STACK *)sk);                                                     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED int sk_##name##_is_sorted(                      \
-      const STACK_OF(name) *sk) {                                              \
+  OPENSSL_INLINE int sk_##name##_is_sorted(const STACK_OF(name) *sk) {         \
     return sk_is_sorted((const _STACK *)sk);                                   \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED stack_##name##_cmp_func                         \
-      sk_##name##_set_cmp_func(STACK_OF(name) *sk,                             \
-                               stack_##name##_cmp_func comp) {                 \
+  OPENSSL_INLINE stack_##name##_cmp_func sk_##name##_set_cmp_func(             \
+      STACK_OF(name) *sk, stack_##name##_cmp_func comp) {                      \
     return (stack_##name##_cmp_func)sk_set_cmp_func((_STACK *)sk,              \
                                                     (stack_cmp_func)comp);     \
   }                                                                            \
                                                                                \
-  static inline OPENSSL_UNUSED STACK_OF(name) *                                \
+  OPENSSL_INLINE STACK_OF(name) *                                              \
       sk_##name##_deep_copy(const STACK_OF(name) *sk,                          \
                             ptrtype(*copy_func)(ptrtype),                      \
                             void (*free_func)(ptrtype)) {                      \
-    return (STACK_OF(name) *)sk_deep_copy((const _STACK *)sk,                  \
-                                          (void *(*)(void *))copy_func,        \
-                                          (void (*)(void *))free_func);        \
+    return (STACK_OF(name) *)sk_deep_copy(                                     \
+        (const _STACK *)sk, sk_##name##_call_copy_func,                        \
+        (stack_copy_func)copy_func, sk_##name##_call_free_func,                \
+        (stack_free_func)free_func);                                           \
   }
 
 // DEFINE_NAMED_STACK_OF defines |STACK_OF(name)| to be a stack whose elements
@@ -393,7 +437,7 @@
 
 #include <type_traits>
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 namespace internal {
 
@@ -410,10 +454,14 @@
 struct DeleterImpl<
     Stack, typename std::enable_if<!StackTraits<Stack>::kIsConst>::type> {
   static void Free(Stack *sk) {
-    sk_pop_free(
-        reinterpret_cast<_STACK *>(sk),
-        reinterpret_cast<void (*)(void *)>(
-            DeleterImpl<typename StackTraits<Stack>::Type>::Free));
+    // sk_FOO_pop_free is defined by macros and bound by name, so we cannot
+    // access it from C++ here.
+    using Type = typename StackTraits<Stack>::Type;
+    sk_pop_free_ex(reinterpret_cast<_STACK *>(sk),
+                   [](stack_free_func /* unused */, void *ptr) {
+                     DeleterImpl<Type>::Free(reinterpret_cast<Type *>(ptr));
+                   },
+                   nullptr);
   }
 };
 
@@ -462,7 +510,7 @@
 // PushToStack pushes |elem| to |sk|. It returns true on success and false on
 // allocation failure.
 template <typename Stack>
-static inline
+inline
     typename std::enable_if<!internal::StackTraits<Stack>::kIsConst, bool>::type
     PushToStack(Stack *sk,
                 UniquePtr<typename internal::StackTraits<Stack>::Type> elem) {
@@ -474,16 +522,16 @@
   return true;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 // Define begin() and end() for stack types so C++ range for loops work.
 template <typename Stack>
-static inline bssl::internal::StackIterator<Stack> begin(const Stack *sk) {
+inline bssl::internal::StackIterator<Stack> begin(const Stack *sk) {
   return bssl::internal::StackIterator<Stack>(sk, 0);
 }
 
 template <typename Stack>
-static inline bssl::internal::StackIterator<Stack> end(const Stack *sk) {
+inline bssl::internal::StackIterator<Stack> end(const Stack *sk) {
   return bssl::internal::StackIterator<Stack>(
       sk, sk_num(reinterpret_cast<const _STACK *>(sk)));
 }
diff --git a/src/include/openssl/thread.h b/src/include/openssl/thread.h
index 98073b0..91706fe 100644
--- a/src/include/openssl/thread.h
+++ b/src/include/openssl/thread.h
@@ -66,7 +66,7 @@
 #endif
 
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 typedef struct crypto_mutex_st {
   char padding;  // Empty structs have different sizes in C and C++.
 } CRYPTO_MUTEX;
diff --git a/src/include/openssl/type_check.h b/src/include/openssl/type_check.h
index da78d70..e5d7047 100644
--- a/src/include/openssl/type_check.h
+++ b/src/include/openssl/type_check.h
@@ -72,10 +72,6 @@
 // CHECKED_CAST casts |p| from type |from| to type |to|.
 #define CHECKED_CAST(to, from, p) ((to) (1 ? (p) : (from)0))
 
-// CHECKED_PTR_OF casts a given pointer to void* and statically checks that it
-// was a pointer to |type|.
-#define CHECKED_PTR_OF(type, p) CHECKED_CAST(void*, type*, (p))
-
 #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
 #define OPENSSL_COMPILE_ASSERT(cond, msg) _Static_assert(cond, #msg)
 #else
diff --git a/src/include/openssl/x509.h b/src/include/openssl/x509.h
index eeab5ec..ee3eccc 100644
--- a/src/include/openssl/x509.h
+++ b/src/include/openssl/x509.h
@@ -1129,7 +1129,7 @@
 #if !defined(BORINGSSL_NO_CXX)
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(NETSCAPE_SPKI, NETSCAPE_SPKI_free)
 BORINGSSL_MAKE_DELETER(RSA_PSS_PARAMS, RSA_PSS_PARAMS_free)
@@ -1158,7 +1158,7 @@
     internal::StackAllocated<X509_STORE_CTX, void, X509_STORE_CTX_zero,
                              X509_STORE_CTX_cleanup>;
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  /* extern C++ */
 #endif  /* !BORINGSSL_NO_CXX */
@@ -1200,5 +1200,6 @@
 #define X509_R_WRONG_TYPE 134
 #define X509_R_NAME_TOO_LONG 135
 #define X509_R_INVALID_PARAMETER 136
+#define X509_R_SIGNATURE_ALGORITHM_MISMATCH 137
 
 #endif
diff --git a/src/include/openssl/x509v3.h b/src/include/openssl/x509v3.h
index 1af439d..7d1c306 100644
--- a/src/include/openssl/x509v3.h
+++ b/src/include/openssl/x509v3.h
@@ -57,7 +57,6 @@
 
 #include <openssl/bio.h>
 #include <openssl/conf.h>
-#include <openssl/lhash.h>
 #include <openssl/x509.h>
 
 #ifdef __cplusplus
@@ -612,7 +611,8 @@
 				  X509V3_CTX *ctx, CONF_VALUE *cnf, int is_nc);
 OPENSSL_EXPORT void X509V3_conf_free(CONF_VALUE *val);
 
-OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_conf_nid(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx, int ext_nid, char *value);
+typedef struct x509_must_be_null_st X509_MUST_BE_NULL;
+OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_conf_nid(X509_MUST_BE_NULL *conf, X509V3_CTX *ctx, int ext_nid, char *value);
 OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_nconf_nid(CONF *conf, X509V3_CTX *ctx, int ext_nid, char *value);
 OPENSSL_EXPORT X509_EXTENSION *X509V3_EXT_nconf(CONF *conf, X509V3_CTX *ctx, char *name, char *value);
 OPENSSL_EXPORT int X509V3_EXT_add_nconf_sk(CONF *conf, X509V3_CTX *ctx, char *section, STACK_OF(X509_EXTENSION) **sk);
@@ -620,9 +620,6 @@
 OPENSSL_EXPORT int X509V3_EXT_REQ_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509_REQ *req);
 OPENSSL_EXPORT int X509V3_EXT_CRL_add_nconf(CONF *conf, X509V3_CTX *ctx, char *section, X509_CRL *crl);
 
-OPENSSL_EXPORT int X509V3_EXT_CRL_add_conf(LHASH_OF(CONF_VALUE) *conf, X509V3_CTX *ctx,
-			    char *section, X509_CRL *crl);
-
 OPENSSL_EXPORT int X509V3_add_value_bool_nf(char *name, int asn1_bool,
 			     STACK_OF(CONF_VALUE) **extlist);
 OPENSSL_EXPORT int X509V3_get_value_bool(CONF_VALUE *value, int *asn1_bool);
@@ -751,7 +748,7 @@
 
 extern "C++" {
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 BORINGSSL_MAKE_DELETER(ACCESS_DESCRIPTION, ACCESS_DESCRIPTION_free)
 BORINGSSL_MAKE_DELETER(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
@@ -760,7 +757,7 @@
 BORINGSSL_MAKE_DELETER(GENERAL_NAME, GENERAL_NAME_free)
 BORINGSSL_MAKE_DELETER(POLICYINFO, POLICYINFO_free)
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 }  /* extern C++ */
 #endif
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index 6881089..d6c1294 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -41,6 +41,8 @@
   tls13_server.cc
 )
 
+add_dependencies(ssl global_target)
+
 target_link_libraries(ssl crypto)
 
 add_executable(
@@ -53,6 +55,8 @@
   $<TARGET_OBJECTS:test_support>
 )
 
+add_dependencies(ssl_test global_target)
+
 target_link_libraries(ssl_test ssl crypto boringssl_gtest)
 if(WIN32)
   target_link_libraries(ssl_test ws2_32)
diff --git a/src/ssl/d1_both.cc b/src/ssl/d1_both.cc
index f22a498..d82852d 100644
--- a/src/ssl/d1_both.cc
+++ b/src/ssl/d1_both.cc
@@ -127,7 +127,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // TODO(davidben): 28 comes from the size of IP + UDP header. Is this reasonable
 // for these values? Notably, why is kMinMTU a function of the transport
@@ -601,15 +601,6 @@
   return add_outgoing(ssl, true /* ChangeCipherSpec */, Array<uint8_t>());
 }
 
-bool dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
-  // The |add_alert| path is only used for warning alerts for now, which DTLS
-  // never sends. This will be implemented later once closure alerts are
-  // converted.
-  assert(false);
-  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-  return false;
-}
-
 // dtls1_update_mtu updates the current MTU from the BIO, ensuring it is above
 // the minimum.
 static void dtls1_update_mtu(SSL *ssl) {
@@ -848,4 +839,4 @@
   return kMinMTU;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/d1_lib.cc b/src/ssl/d1_lib.cc
index d73e538..0e0b211 100644
--- a/src/ssl/d1_lib.cc
+++ b/src/ssl/d1_lib.cc
@@ -68,7 +68,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // DTLS1_MTU_TIMEOUTS is the maximum number of timeouts to expire
 // before starting to decrease the MTU.
@@ -187,7 +187,7 @@
   return true;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/d1_pkt.cc b/src/ssl/d1_pkt.cc
index a694c5f..be595b0 100644
--- a/src/ssl/d1_pkt.cc
+++ b/src/ssl/d1_pkt.cc
@@ -126,7 +126,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 ssl_open_record_t dtls1_open_app_data(SSL *ssl, Span<uint8_t> *out,
                                       size_t *out_consumed, uint8_t *out_alert,
@@ -271,4 +271,4 @@
   return 1;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/dtls_method.cc b/src/ssl/dtls_method.cc
index 8d40edf..d49687f 100644
--- a/src/ssl/dtls_method.cc
+++ b/src/ssl/dtls_method.cc
@@ -121,7 +121,6 @@
     dtls1_finish_message,
     dtls1_add_message,
     dtls1_add_change_cipher_spec,
-    dtls1_add_alert,
     dtls1_flush_flight,
     dtls1_on_handshake_complete,
     dtls1_set_read_state,
diff --git a/src/ssl/dtls_record.cc b/src/ssl/dtls_record.cc
index d348601..992fb52 100644
--- a/src/ssl/dtls_record.cc
+++ b/src/ssl/dtls_record.cc
@@ -121,7 +121,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // to_u64_be treats |in| as a 8-byte big-endian integer and returns the value as
 // a |uint64_t|.
@@ -137,13 +137,13 @@
 
 // dtls1_bitmap_should_discard returns one if |seq_num| has been seen in
 // |bitmap| or is stale. Otherwise it returns zero.
-static int dtls1_bitmap_should_discard(DTLS1_BITMAP *bitmap,
-                                       const uint8_t seq_num[8]) {
+static bool dtls1_bitmap_should_discard(DTLS1_BITMAP *bitmap,
+                                        const uint8_t seq_num[8]) {
   const unsigned kWindowSize = sizeof(bitmap->map) * 8;
 
   uint64_t seq_num_u = to_u64_be(seq_num);
   if (seq_num_u > bitmap->max_seq_num) {
-    return 0;
+    return false;
   }
   uint64_t idx = bitmap->max_seq_num - seq_num_u;
   return idx >= kWindowSize || (bitmap->map & (((uint64_t)1) << idx));
@@ -291,14 +291,14 @@
          get_write_aead(ssl, use_epoch)->ExplicitNonceLen();
 }
 
-int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
-                     uint8_t type, const uint8_t *in, size_t in_len,
-                     enum dtls1_use_epoch_t use_epoch) {
+bool dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+                      uint8_t type, const uint8_t *in, size_t in_len,
+                      enum dtls1_use_epoch_t use_epoch) {
   const size_t prefix = dtls_seal_prefix_len(ssl, use_epoch);
   if (buffers_alias(in, in_len, out, max_out) &&
       (max_out < prefix || out + prefix != in)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
-    return 0;
+    return false;
   }
 
   // Determine the parameters for the current epoch.
@@ -314,7 +314,7 @@
 
   if (max_out < DTLS1_RT_HEADER_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
-    return 0;
+    return false;
   }
 
   out[0] = type;
@@ -330,7 +330,7 @@
   size_t ciphertext_len;
   if (!aead->CiphertextLen(&ciphertext_len, in_len, 0)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE);
-    return 0;
+    return false;
   }
   out[11] = ciphertext_len >> 8;
   out[12] = ciphertext_len & 0xff;
@@ -341,13 +341,13 @@
                   max_out - DTLS1_RT_HEADER_LENGTH, type, record_version,
                   &out[3] /* seq */, header, in, in_len) ||
       !ssl_record_sequence_update(&seq[2], 6)) {
-    return 0;
+    return false;
   }
   assert(ciphertext_len == len_copy);
 
   *out_len = DTLS1_RT_HEADER_LENGTH + ciphertext_len;
   ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, header);
-  return 1;
+  return true;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/handoff.cc b/src/ssl/handoff.cc
index 68cac5b..a47b7c1 100644
--- a/src/ssl/handoff.cc
+++ b/src/ssl/handoff.cc
@@ -19,7 +19,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 constexpr int kHandoffVersion = 0;
 constexpr int kHandbackVersion = 0;
@@ -359,4 +359,4 @@
   return CBS_len(&seq) == 0;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc
index 8e5c62c..963038f 100644
--- a/src/ssl/handshake.cc
+++ b/src/ssl/handshake.cc
@@ -122,7 +122,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg)
     : ssl(ssl_arg),
@@ -667,4 +667,4 @@
   }
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/handshake_client.cc b/src/ssl/handshake_client.cc
index 9f9e483..e46b39f 100644
--- a/src/ssl/handshake_client.cc
+++ b/src/ssl/handshake_client.cc
@@ -166,12 +166,13 @@
 #include <openssl/md5.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
+#include <openssl/sha.h>
 
 #include "../crypto/internal.h"
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 enum ssl_client_hs_state_t {
   state_start_connect = 0,
@@ -212,20 +213,20 @@
   }
 }
 
-static int ssl_write_client_cipher_list(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ssl_write_client_cipher_list(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   uint32_t mask_a, mask_k;
   ssl_get_client_disabled(hs, &mask_a, &mask_k);
 
   CBB child;
   if (!CBB_add_u16_length_prefixed(out, &child)) {
-    return 0;
+    return false;
   }
 
   // Add a fake cipher suite. See draft-davidben-tls-grease-01.
   if (ssl->ctx->grease_enabled &&
       !CBB_add_u16(&child, ssl_get_grease_value(hs, ssl_grease_cipher))) {
-    return 0;
+    return false;
   }
 
   // Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on
@@ -233,20 +234,20 @@
   if (hs->max_version >= TLS1_3_VERSION) {
     if (!EVP_has_aes_hardware() &&
         !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
-      return 0;
+      return false;
     }
     if (!CBB_add_u16(&child, TLS1_CK_AES_128_GCM_SHA256 & 0xffff) ||
         !CBB_add_u16(&child, TLS1_CK_AES_256_GCM_SHA384 & 0xffff)) {
-      return 0;
+      return false;
     }
     if (EVP_has_aes_hardware() &&
         !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) {
-      return 0;
+      return false;
     }
   }
 
   if (hs->min_version < TLS1_3_VERSION) {
-    int any_enabled = 0;
+    bool any_enabled = false;
     for (const SSL_CIPHER *cipher : SSL_get_ciphers(ssl)) {
       // Skip disabled ciphers
       if ((cipher->algorithm_mkey & mask_k) ||
@@ -257,53 +258,53 @@
           SSL_CIPHER_get_max_version(cipher) < hs->min_version) {
         continue;
       }
-      any_enabled = 1;
+      any_enabled = true;
       if (!CBB_add_u16(&child, ssl_cipher_get_value(cipher))) {
-        return 0;
+        return false;
       }
     }
 
     // If all ciphers were disabled, return the error to the caller.
     if (!any_enabled && hs->max_version < TLS1_3_VERSION) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE);
-      return 0;
+      return false;
     }
   }
 
   if (ssl->mode & SSL_MODE_SEND_FALLBACK_SCSV) {
     if (!CBB_add_u16(&child, SSL3_CK_FALLBACK_SCSV & 0xffff)) {
-      return 0;
+      return false;
     }
   }
 
   return CBB_flush(out);
 }
 
-int ssl_write_client_hello(SSL_HANDSHAKE *hs) {
+bool ssl_write_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   ScopedCBB cbb;
   CBB body;
   if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO)) {
-    return 0;
+    return false;
   }
 
   CBB child;
   if (!CBB_add_u16(&body, hs->client_version) ||
       !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
       !CBB_add_u8_length_prefixed(&body, &child)) {
-    return 0;
+    return false;
   }
 
   // Do not send a session ID on renegotiation.
   if (!ssl->s3->initial_handshake_complete &&
       !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
-    return 0;
+    return false;
   }
 
   if (SSL_is_dtls(ssl)) {
     if (!CBB_add_u8_length_prefixed(&body, &child) ||
         !CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) {
-      return 0;
+      return false;
     }
   }
 
@@ -313,19 +314,19 @@
       !CBB_add_u8(&body, 1 /* one compression method */) ||
       !CBB_add_u8(&body, 0 /* null compression */) ||
       !ssl_add_clienthello_tlsext(hs, &body, header_len + CBB_len(&body))) {
-    return 0;
+    return false;
   }
 
   Array<uint8_t> msg;
   if (!ssl->method->finish_message(ssl, cbb.get(), &msg)) {
-    return 0;
+    return false;
   }
 
   // Now that the length prefixes have been computed, fill in the placeholder
   // PSK binder.
   if (hs->needs_psk_binder &&
       !tls13_write_psk_binder(hs, msg.data(), msg.size())) {
-    return 0;
+    return false;
   }
 
   return ssl->method->add_message(ssl, std::move(msg));
@@ -599,7 +600,7 @@
             .subspan(SSL3_RANDOM_SIZE - sizeof(kTLS13DowngradeRandom));
     if (suffix == kTLS12DowngradeRandom || suffix == kTLS13DowngradeRandom) {
       ssl->s3->tls13_downgrade = true;
-      if (!ssl->ctx->ignore_tls13_downgrade) {
+      if (!hs->config->ignore_tls13_downgrade) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_TLS13_DOWNGRADE);
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
         return ssl_hs_error;
@@ -607,7 +608,7 @@
     }
   }
 
-  if (!ssl->s3->initial_handshake_complete && ssl->session != NULL &&
+  if (!ssl->s3->initial_handshake_complete && ssl->session != nullptr &&
       ssl->session->session_id_length != 0 &&
       CBS_mem_equal(&session_id, ssl->session->session_id,
                     ssl->session->session_id_length)) {
@@ -1606,14 +1607,11 @@
   }
   session->ticket_lifetime_hint = ticket_lifetime_hint;
 
-  // Generate a session ID for this session based on the session ticket. We use
-  // the session ID mechanism for detecting ticket resumption. This also fits in
-  // with assumptions elsewhere in OpenSSL.
-  if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket),
-                  session->session_id, &session->session_id_length,
-                  EVP_sha256(), NULL)) {
-    return ssl_hs_error;
-  }
+  // Generate a session ID for this session. Some callers expect all sessions to
+  // have a session ID. Additionally, it acts as the session ID to signal
+  // resumption.
+  SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id);
+  session->session_id_length = SHA256_DIGEST_LENGTH;
 
   if (renewed_session) {
     session->not_resumable = false;
@@ -1822,4 +1820,4 @@
   return "TLS client unknown";
 }
 
-}
+BSSL_NAMESPACE_END
diff --git a/src/ssl/handshake_server.cc b/src/ssl/handshake_server.cc
index f0ed0d8..c546088 100644
--- a/src/ssl/handshake_server.cc
+++ b/src/ssl/handshake_server.cc
@@ -170,7 +170,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 bool ssl_client_cipher_list_contains_cipher(
     const SSL_CLIENT_HELLO *client_hello, uint16_t id) {
@@ -1626,4 +1626,4 @@
   return "TLS server unknown";
 }
 
-}
+BSSL_NAMESPACE_END
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 087f5fb..561b5d9 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -173,7 +173,7 @@
 #endif
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 struct SSL_CONFIG;
 struct SSL_HANDSHAKE;
@@ -414,7 +414,7 @@
 
 // Cipher suites.
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 struct ssl_cipher_st {
   // name is the OpenSSL name for the cipher.
@@ -432,7 +432,7 @@
   uint32_t algorithm_prf;
 };
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // Bits for |algorithm_mkey| (key exchange algorithm).
 #define SSL_kRSA 0x00000001u
@@ -522,7 +522,7 @@
 bool ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
                              size_t *out_mac_secret_len,
                              size_t *out_fixed_iv_len, const SSL_CIPHER *cipher,
-                             uint16_t version, int is_dtls);
+                             uint16_t version, bool is_dtls);
 
 // ssl_get_handshake_digest returns the |EVP_MD| corresponding to |version| and
 // |cipher|.
@@ -659,7 +659,7 @@
   // resulting object, depending on |direction|. |version| is the normalized
   // protocol version, so DTLS 1.0 is represented as 0x0301, not 0xffef.
   static UniquePtr<SSLAEADContext> Create(enum evp_aead_direction_t direction,
-                                          uint16_t version, int is_dtls,
+                                          uint16_t version, bool is_dtls,
                                           const SSL_CIPHER *cipher,
                                           Span<const uint8_t> enc_key,
                                           Span<const uint8_t> mac_key,
@@ -797,8 +797,8 @@
 // Record layer.
 
 // ssl_record_sequence_update increments the sequence number in |seq|. It
-// returns one on success and zero on wraparound.
-int ssl_record_sequence_update(uint8_t *seq, size_t seq_len);
+// returns true on success and false on wraparound.
+bool ssl_record_sequence_update(uint8_t *seq, size_t seq_len);
 
 // ssl_record_prefix_len returns the length of the prefix before the ciphertext
 // of a record for |ssl|.
@@ -863,9 +863,9 @@
 size_t ssl_seal_align_prefix_len(const SSL *ssl);
 
 // tls_seal_record seals a new record of type |type| and body |in| and writes it
-// to |out|. At most |max_out| bytes will be written. It returns one on success
-// and zero on error. If enabled, |tls_seal_record| implements TLS 1.0 CBC 1/n-1
-// record splitting and may write two records concatenated.
+// to |out|. At most |max_out| bytes will be written. It returns true on success
+// and false on error. If enabled, |tls_seal_record| implements TLS 1.0 CBC
+// 1/n-1 record splitting and may write two records concatenated.
 //
 // For a large record, the bulk of the ciphertext will begin
 // |ssl_seal_align_prefix_len| bytes into out. Aligning |out| appropriately may
@@ -873,8 +873,8 @@
 // bytes to |out|.
 //
 // |in| and |out| may not alias.
-int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
-                    uint8_t type, const uint8_t *in, size_t in_len);
+bool tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+                     uint8_t type, const uint8_t *in, size_t in_len);
 
 enum dtls1_use_epoch_t {
   dtls1_use_previous_epoch,
@@ -893,9 +893,9 @@
 // which epoch's cipher state to use. Unlike |tls_seal_record|, |in| and |out|
 // may alias but, if they do, |in| must be exactly |dtls_seal_prefix_len| bytes
 // ahead of |out|.
-int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
-                     uint8_t type, const uint8_t *in, size_t in_len,
-                     enum dtls1_use_epoch_t use_epoch);
+bool dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
+                      uint8_t type, const uint8_t *in, size_t in_len,
+                      enum dtls1_use_epoch_t use_epoch);
 
 // ssl_process_alert processes |in| as an alert and updates |ssl|'s shutdown
 // state. It returns one of |ssl_open_record_discard|, |ssl_open_record_error|,
@@ -907,9 +907,8 @@
 
 // Private key operations.
 
-// ssl_has_private_key returns one if |cfg| has a private key configured and
-// zero otherwise.
-int ssl_has_private_key(const SSL_CONFIG *cfg);
+// ssl_has_private_key returns whether |cfg| has a private key configured.
+bool ssl_has_private_key(const SSL_CONFIG *cfg);
 
 // ssl_private_key_* perform the corresponding operation on
 // |SSL_PRIVATE_KEY_METHOD|. If there is a custom private key configured, they
@@ -990,14 +989,14 @@
 };
 
 // ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it
-// sets |*out_group_id| to the group ID and returns one. Otherwise, it returns
-// zero.
-int ssl_nid_to_group_id(uint16_t *out_group_id, int nid);
+// sets |*out_group_id| to the group ID and returns true. Otherwise, it returns
+// false.
+bool ssl_nid_to_group_id(uint16_t *out_group_id, int nid);
 
-// ssl_name_to_group_id looks up the group corresponding to the |name| string
-// of length |len|. On success, it sets |*out_group_id| to the group ID and
-// returns one. Otherwise, it returns zero.
-int ssl_name_to_group_id(uint16_t *out_group_id, const char *name, size_t len);
+// ssl_name_to_group_id looks up the group corresponding to the |name| string of
+// length |len|. On success, it sets |*out_group_id| to the group ID and returns
+// true. Otherwise, it returns false.
+bool ssl_name_to_group_id(uint16_t *out_group_id, const char *name, size_t len);
 
 
 // Handshake messages.
@@ -1146,9 +1145,9 @@
 
 // Certificate functions.
 
-// ssl_has_certificate returns one if a certificate and private key are
-// configured and zero otherwise.
-int ssl_has_certificate(const SSL_CONFIG *cfg);
+// ssl_has_certificate returns whether a certificate and private key are
+// configured.
+bool ssl_has_certificate(const SSL_CONFIG *cfg);
 
 // ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
 // by a TLS Certificate message. On success, it advances |cbs| and returns
@@ -1169,14 +1168,14 @@
 
 // ssl_add_cert_chain adds |hs->ssl|'s certificate chain to |cbb| in the format
 // used by a TLS Certificate message. If there is no certificate chain, it emits
-// an empty certificate list. It returns one on success and zero on error.
-int ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb);
+// an empty certificate list. It returns true on success and false on error.
+bool ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb);
 
 // ssl_cert_check_digital_signature_key_usage parses the DER-encoded, X.509
-// certificate in |in| and returns one if doesn't specify a key usage or, if it
-// does, if it includes digitalSignature. Otherwise it pushes to the error
-// queue and returns zero.
-int ssl_cert_check_digital_signature_key_usage(const CBS *in);
+// certificate in |in| and returns true if doesn't specify a key usage or, if it
+// does, if it includes digitalSignature. Otherwise it pushes to the error queue
+// and returns false.
+bool ssl_cert_check_digital_signature_key_usage(const CBS *in);
 
 // ssl_cert_parse_pubkey extracts the public key from the DER-encoded, X.509
 // certificate in |in|. It returns an allocated |EVP_PKEY| or else returns
@@ -1195,80 +1194,80 @@
 bool ssl_has_client_CAs(const SSL_CONFIG *cfg);
 
 // ssl_add_client_CA_list adds the configured CA list to |cbb| in the format
-// used by a TLS CertificateRequest message. It returns one on success and zero
-// on error.
-int ssl_add_client_CA_list(SSL_HANDSHAKE *hs, CBB *cbb);
+// used by a TLS CertificateRequest message. It returns true on success and
+// false on error.
+bool ssl_add_client_CA_list(SSL_HANDSHAKE *hs, CBB *cbb);
 
 // ssl_check_leaf_certificate returns one if |pkey| and |leaf| are suitable as
 // a server's leaf certificate for |hs|. Otherwise, it returns zero and pushes
 // an error on the error queue.
-int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
+bool ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
                                const CRYPTO_BUFFER *leaf);
 
 // ssl_on_certificate_selected is called once the certificate has been selected.
 // It finalizes the certificate and initializes |hs->local_pubkey|. It returns
-// one on success and zero on error.
-int ssl_on_certificate_selected(SSL_HANDSHAKE *hs);
+// true on success and false on error.
+bool ssl_on_certificate_selected(SSL_HANDSHAKE *hs);
 
 
 // TLS 1.3 key derivation.
 
 // tls13_init_key_schedule initializes the handshake hash and key derivation
 // state, and incorporates the PSK. The cipher suite and PRF hash must have been
-// selected at this point. It returns one on success and zero on error.
-int tls13_init_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
-                            size_t psk_len);
+// selected at this point. It returns true on success and false on error.
+bool tls13_init_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
+                             size_t psk_len);
 
 // tls13_init_early_key_schedule initializes the handshake hash and key
 // derivation state from the resumption secret and incorporates the PSK to
 // derive the early secrets. It returns one on success and zero on error.
-int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
-                                  size_t psk_len);
+bool tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
+                                   size_t psk_len);
 
 // tls13_advance_key_schedule incorporates |in| into the key schedule with
-// HKDF-Extract. It returns one on success and zero on error.
-int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
+// HKDF-Extract. It returns true on success and false on error.
+bool tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
                                size_t len);
 
 // tls13_set_traffic_key sets the read or write traffic keys to
-// |traffic_secret|. It returns one on success and zero on error.
-int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
-                          const uint8_t *traffic_secret,
-                          size_t traffic_secret_len);
+// |traffic_secret|. It returns true on success and false on error.
+bool tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
+                           const uint8_t *traffic_secret,
+                           size_t traffic_secret_len);
 
-// tls13_derive_early_secrets derives the early traffic secret. It returns one
-// on success and zero on error.
-int tls13_derive_early_secrets(SSL_HANDSHAKE *hs);
+// tls13_derive_early_secrets derives the early traffic secret. It returns true
+// on success and false on error.
+bool tls13_derive_early_secrets(SSL_HANDSHAKE *hs);
 
 // tls13_derive_handshake_secrets derives the handshake traffic secret. It
-// returns one on success and zero on error.
-int tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs);
+// returns true on success and false on error.
+bool tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs);
 
 // tls13_rotate_traffic_key derives the next read or write traffic secret. It
-// returns one on success and zero on error.
-int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction);
+// returns true on success and false on error.
+bool tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction);
 
 // tls13_derive_application_secrets derives the initial application data traffic
 // and exporter secrets based on the handshake transcripts and |master_secret|.
-// It returns one on success and zero on error.
-int tls13_derive_application_secrets(SSL_HANDSHAKE *hs);
+// It returns true on success and false on error.
+bool tls13_derive_application_secrets(SSL_HANDSHAKE *hs);
 
 // tls13_derive_resumption_secret derives the |resumption_secret|.
-int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs);
+bool tls13_derive_resumption_secret(SSL_HANDSHAKE *hs);
 
 // tls13_export_keying_material provides an exporter interface to use the
 // |exporter_secret|.
-int tls13_export_keying_material(SSL *ssl, Span<uint8_t> out,
-                                 Span<const uint8_t> secret,
-                                 Span<const char> label,
-                                 Span<const uint8_t> context);
+bool tls13_export_keying_material(SSL *ssl, Span<uint8_t> out,
+                                  Span<const uint8_t> secret,
+                                  Span<const char> label,
+                                  Span<const uint8_t> context);
 
 // tls13_finished_mac calculates the MAC of the handshake transcript to verify
 // the integrity of the Finished message, and stores the result in |out| and
-// length in |out_len|. |is_server| is 1 if this is for the Server Finished and
-// 0 for the Client Finished.
-int tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
-                       int is_server);
+// length in |out_len|. |is_server| is true if this is for the Server Finished
+// and false for the Client Finished.
+bool tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
+                        bool is_server);
 
 // tls13_derive_session_psk calculates the PSK for this session based on the
 // resumption master secret and |nonce|. It returns true on success, and false
@@ -1276,15 +1275,15 @@
 bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce);
 
 // tls13_write_psk_binder calculates the PSK binder value and replaces the last
-// bytes of |msg| with the resulting value. It returns 1 on success, and 0 on
-// failure.
-int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len);
+// bytes of |msg| with the resulting value. It returns true on success, and
+// false on failure.
+bool tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len);
 
-// tls13_verify_psk_binder verifies that the handshake transcript, truncated
-// up to the binders has a valid signature using the value of |session|'s
-// resumption secret. It returns 1 on success, and 0 on failure.
-int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
-                            const SSLMessage &msg, CBS *binders);
+// tls13_verify_psk_binder verifies that the handshake transcript, truncated up
+// to the binders has a valid signature using the value of |session|'s
+// resumption secret. It returns true on success, and false on failure.
+bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
+                             const SSLMessage &msg, CBS *binders);
 
 
 // Handshake functions.
@@ -1616,29 +1615,29 @@
 const char *tls13_client_handshake_state(SSL_HANDSHAKE *hs);
 const char *tls13_server_handshake_state(SSL_HANDSHAKE *hs);
 
-// tls13_post_handshake processes a post-handshake message. It returns one on
-// success and zero on failure.
-int tls13_post_handshake(SSL *ssl, const SSLMessage &msg);
+// tls13_post_handshake processes a post-handshake message. It returns true on
+// success and false on failure.
+bool tls13_post_handshake(SSL *ssl, const SSLMessage &msg);
 
-int tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
-                              int allow_anonymous);
-int tls13_process_certificate_verify(SSL_HANDSHAKE *hs, const SSLMessage &msg);
+bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
+                               bool allow_anonymous);
+bool tls13_process_certificate_verify(SSL_HANDSHAKE *hs, const SSLMessage &msg);
 
 // tls13_process_finished processes |msg| as a Finished message from the
-// peer. If |use_saved_value| is one, the verify_data is compared against
+// peer. If |use_saved_value| is true, the verify_data is compared against
 // |hs->expected_client_finished| rather than computed fresh.
-int tls13_process_finished(SSL_HANDSHAKE *hs, const SSLMessage &msg,
-                           int use_saved_value);
+bool tls13_process_finished(SSL_HANDSHAKE *hs, const SSLMessage &msg,
+                            bool use_saved_value);
 
-int tls13_add_certificate(SSL_HANDSHAKE *hs);
+bool tls13_add_certificate(SSL_HANDSHAKE *hs);
 
 // tls13_add_certificate_verify adds a TLS 1.3 CertificateVerify message to the
 // handshake. If it returns |ssl_private_key_retry|, it should be called again
 // to retry when the signing operation is completed.
 enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs);
 
-int tls13_add_finished(SSL_HANDSHAKE *hs);
-int tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg);
+bool tls13_add_finished(SSL_HANDSHAKE *hs);
+bool tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg);
 
 bool ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs,
                                          Array<uint8_t> *out_secret,
@@ -1660,7 +1659,7 @@
 // returns whether it's valid.
 bool ssl_is_sct_list_valid(const CBS *contents);
 
-int ssl_write_client_hello(SSL_HANDSHAKE *hs);
+bool ssl_write_client_hello(SSL_HANDSHAKE *hs);
 
 enum ssl_cert_verify_context_t {
   ssl_cert_verify_server,
@@ -1890,9 +1889,6 @@
   // add_change_cipher_spec adds a ChangeCipherSpec record to the pending
   // flight. It returns true on success and false on error.
   bool (*add_change_cipher_spec)(SSL *ssl);
-  // add_alert adds an alert to the pending flight. It returns true on success
-  // and false on error.
-  bool (*add_alert)(SSL *ssl, uint8_t level, uint8_t desc);
   // flush_flight flushes the pending flight to the transport. It returns one on
   // success and <= 0 on error.
   int (*flush_flight)(SSL *ssl);
@@ -2015,13 +2011,13 @@
   uint16_t alg_id = 0;
 };
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 DECLARE_LHASH_OF(SSL_SESSION)
 
 DEFINE_NAMED_STACK_OF(CertCompressionAlg, bssl::CertCompressionAlg);
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // An ssl_shutdown_t describes the shutdown state of one end of the connection,
 // whether it is alive or has been shutdown via close_notify or fatal alert.
@@ -2460,6 +2456,10 @@
   // shed_handshake_config indicates that the handshake config (this object!)
   // should be freed after the handshake completes.
   bool shed_handshake_config : 1;
+
+  // ignore_tls13_downgrade is whether the connection should continue when the
+  // server random signals a downgrade.
+  bool ignore_tls13_downgrade:1;
 };
 
 // From RFC 8446, used in determining PSK modes.
@@ -2476,14 +2476,14 @@
 
 UniquePtr<CERT> ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *cert);
-int ssl_set_cert(CERT *cert, UniquePtr<CRYPTO_BUFFER> buffer);
-int ssl_is_key_type_supported(int key_type);
-// ssl_compare_public_and_private_key returns one if |pubkey| is the public
-// counterpart to |privkey|. Otherwise it returns zero and pushes a helpful
+bool ssl_set_cert(CERT *cert, UniquePtr<CRYPTO_BUFFER> buffer);
+bool ssl_is_key_type_supported(int key_type);
+// ssl_compare_public_and_private_key returns true if |pubkey| is the public
+// counterpart to |privkey|. Otherwise it returns false and pushes a helpful
 // message on the error queue.
-int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
+bool ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
                                        const EVP_PKEY *privkey);
-int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey);
+bool ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey);
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server);
 int ssl_encrypt_ticket(SSL_HANDSHAKE *hs, CBB *out, const SSL_SESSION *session);
 int ssl_ctx_rotate_ticket_encryption_key(SSL_CTX *ctx);
@@ -2590,14 +2590,12 @@
 bool ssl3_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg);
 bool ssl3_add_message(SSL *ssl, Array<uint8_t> msg);
 bool ssl3_add_change_cipher_spec(SSL *ssl);
-bool ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc);
 int ssl3_flush_flight(SSL *ssl);
 
 bool dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
 bool dtls1_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg);
 bool dtls1_add_message(SSL *ssl, Array<uint8_t> msg);
 bool dtls1_add_change_cipher_spec(SSL *ssl);
-bool dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc);
 int dtls1_flush_flight(SSL *ssl);
 
 // ssl_add_message_cbb finishes the handshake message in |cbb| and adds it to
@@ -2749,7 +2747,7 @@
 // current state of the error queue.
 void ssl_set_read_error(SSL *ssl);
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 
 // Opaque C types.
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc
index 98896a3..3f09d50 100644
--- a/src/ssl/s3_both.cc
+++ b/src/ssl/s3_both.cc
@@ -130,7 +130,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 static bool add_record_to_flight(SSL *ssl, uint8_t type,
                                  Span<const uint8_t> in) {
@@ -266,18 +266,6 @@
   return true;
 }
 
-bool ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
-  uint8_t alert[2] = {level, desc};
-  if (!tls_flush_pending_hs_data(ssl) ||
-      !add_record_to_flight(ssl, SSL3_RT_ALERT, alert)) {
-    return false;
-  }
-
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, alert);
-  ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, ((int)level << 8) | desc);
-  return true;
-}
-
 int ssl3_flush_flight(SSL *ssl) {
   if (!tls_flush_pending_hs_data(ssl)) {
     return -1;
@@ -636,4 +624,4 @@
   }
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/s3_lib.cc b/src/ssl/s3_lib.cc
index 0b24f94..0e0770c 100644
--- a/src/ssl/s3_lib.cc
+++ b/src/ssl/s3_lib.cc
@@ -162,7 +162,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 SSL3_STATE::SSL3_STATE()
     : skip_early_data(false),
@@ -215,4 +215,4 @@
   ssl->s3 = NULL;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/s3_pkt.cc b/src/ssl/s3_pkt.cc
index 50e709b..1ccbf9f 100644
--- a/src/ssl/s3_pkt.cc
+++ b/src/ssl/s3_pkt.cc
@@ -122,7 +122,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 static int do_ssl3_write(SSL *ssl, int type, const uint8_t *in, unsigned len);
 
@@ -425,4 +425,4 @@
   return 1;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/span_test.cc b/src/ssl/span_test.cc
index 0aa7f3d..7db3d70 100644
--- a/src/ssl/span_test.cc
+++ b/src/ssl/span_test.cc
@@ -19,7 +19,7 @@
 
 #include <openssl/ssl.h>
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 namespace {
 
 static void TestCtor(Span<int> s, const int *ptr, size_t size) {
@@ -87,4 +87,4 @@
 }
 
 }  // namespace
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/ssl_aead_ctx.cc b/src/ssl/ssl_aead_ctx.cc
index 322b1b5..335f6f4 100644
--- a/src/ssl/ssl_aead_ctx.cc
+++ b/src/ssl/ssl_aead_ctx.cc
@@ -31,7 +31,7 @@
 #define FUZZER_MODE false
 #endif
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 SSLAEADContext::SSLAEADContext(uint16_t version_arg, bool is_dtls_arg,
                                const SSL_CIPHER *cipher_arg)
@@ -55,7 +55,7 @@
 }
 
 UniquePtr<SSLAEADContext> SSLAEADContext::Create(
-    enum evp_aead_direction_t direction, uint16_t version, int is_dtls,
+    enum evp_aead_direction_t direction, uint16_t version, bool is_dtls,
     const SSL_CIPHER *cipher, Span<const uint8_t> enc_key,
     Span<const uint8_t> mac_key, Span<const uint8_t> fixed_iv) {
   const EVP_AEAD *aead;
@@ -433,4 +433,4 @@
          EVP_AEAD_CTX_get_iv(ctx_.get(), out_iv, out_iv_len);
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/ssl_asn1.cc b/src/ssl/ssl_asn1.cc
index 5dfacb2..caccef4 100644
--- a/src/ssl/ssl_asn1.cc
+++ b/src/ssl/ssl_asn1.cc
@@ -104,7 +104,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // An SSL_SESSION is serialized as the following ASN.1 structure:
 //
@@ -751,7 +751,7 @@
   return SSL_SESSION_to_bytes_full(in, cbb, 0);
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/ssl_buffer.cc b/src/ssl/ssl_buffer.cc
index 72647a4..b94f081 100644
--- a/src/ssl/ssl_buffer.cc
+++ b/src/ssl/ssl_buffer.cc
@@ -27,7 +27,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // BIO uses int instead of size_t. No lengths will exceed uint16_t, so this will
 // not overflow.
@@ -284,4 +284,4 @@
   }
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/ssl_cert.cc b/src/ssl/ssl_cert.cc
index 4842974..37d6501 100644
--- a/src/ssl/ssl_cert.cc
+++ b/src/ssl/ssl_cert.cc
@@ -133,7 +133,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 CERT::CERT(const SSL_X509_METHOD *x509_method_arg)
     : x509_method(x509_method_arg) {}
@@ -289,10 +289,10 @@
   return 1;
 }
 
-int ssl_set_cert(CERT *cert, UniquePtr<CRYPTO_BUFFER> buffer) {
+bool ssl_set_cert(CERT *cert, UniquePtr<CRYPTO_BUFFER> buffer) {
   switch (check_leaf_cert_and_privkey(buffer.get(), cert->privatekey.get())) {
     case leaf_cert_and_privkey_error:
-      return 0;
+      return false;
     case leaf_cert_and_privkey_mismatch:
       // don't fail for a cert/key mismatch, just free current private key
       // (when switching to a different cert & key, first this function should
@@ -308,23 +308,23 @@
   if (cert->chain != nullptr) {
     CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(cert->chain.get(), 0));
     sk_CRYPTO_BUFFER_set(cert->chain.get(), 0, buffer.release());
-    return 1;
+    return true;
   }
 
   cert->chain.reset(sk_CRYPTO_BUFFER_new_null());
   if (cert->chain == nullptr) {
-    return 0;
+    return false;
   }
 
   if (!PushToStack(cert->chain.get(), std::move(buffer))) {
     cert->chain.reset();
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-int ssl_has_certificate(const SSL_CONFIG *cfg) {
+bool ssl_has_certificate(const SSL_CONFIG *cfg) {
   return cfg->cert->chain != nullptr &&
          sk_CRYPTO_BUFFER_value(cfg->cert->chain.get(), 0) != nullptr &&
          ssl_has_private_key(cfg);
@@ -394,7 +394,7 @@
   return true;
 }
 
-int ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb) {
+bool ssl_add_cert_chain(SSL_HANDSHAKE *hs, CBB *cbb) {
   if (!ssl_has_certificate(hs->config)) {
     return CBB_add_u24(cbb, 0);
   }
@@ -402,7 +402,7 @@
   CBB certs;
   if (!CBB_add_u24_length_prefixed(cbb, &certs)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   STACK_OF(CRYPTO_BUFFER) *chain = hs->config->cert->chain.get();
@@ -414,7 +414,7 @@
                        CRYPTO_BUFFER_len(buffer)) ||
         !CBB_flush(&certs)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
+      return false;
     }
   }
 
@@ -424,7 +424,7 @@
 // ssl_cert_skip_to_spki parses a DER-encoded, X.509 certificate from |in| and
 // positions |*out_tbs_cert| to cover the TBSCertificate, starting at the
 // subjectPublicKeyInfo.
-static int ssl_cert_skip_to_spki(const CBS *in, CBS *out_tbs_cert) {
+static bool ssl_cert_skip_to_spki(const CBS *in, CBS *out_tbs_cert) {
   /* From RFC 5280, section 4.1
    *    Certificate  ::=  SEQUENCE  {
    *      tbsCertificate       TBSCertificate,
@@ -460,10 +460,10 @@
       !CBS_get_asn1(out_tbs_cert, NULL, CBS_ASN1_SEQUENCE) ||
       // subject
       !CBS_get_asn1(out_tbs_cert, NULL, CBS_ASN1_SEQUENCE)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 UniquePtr<EVP_PKEY> ssl_cert_parse_pubkey(const CBS *in) {
@@ -476,47 +476,42 @@
   return UniquePtr<EVP_PKEY>(EVP_parse_public_key(&tbs_cert));
 }
 
-int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
-                                       const EVP_PKEY *privkey) {
+bool ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
+                                        const EVP_PKEY *privkey) {
   if (EVP_PKEY_is_opaque(privkey)) {
     // We cannot check an opaque private key and have to trust that it
     // matches.
-    return 1;
+    return true;
   }
 
-  int ret = 0;
-
   switch (EVP_PKEY_cmp(pubkey, privkey)) {
     case 1:
-      ret = 1;
-      break;
+      return true;
     case 0:
       OPENSSL_PUT_ERROR(X509, X509_R_KEY_VALUES_MISMATCH);
-      break;
+      return false;
     case -1:
       OPENSSL_PUT_ERROR(X509, X509_R_KEY_TYPE_MISMATCH);
-      break;
+      return false;
     case -2:
       OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_KEY_TYPE);
-      break;
-    default:
-      assert(0);
-      break;
+      return false;
   }
 
-  return ret;
+  assert(0);
+  return false;
 }
 
-int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey) {
+bool ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey) {
   if (privkey == nullptr) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
-    return 0;
+    return false;
   }
 
   if (cert->chain == nullptr ||
       sk_CRYPTO_BUFFER_value(cert->chain.get(), 0) == nullptr) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
-    return 0;
+    return false;
   }
 
   CBS cert_cbs;
@@ -525,13 +520,13 @@
   UniquePtr<EVP_PKEY> pubkey = ssl_cert_parse_pubkey(&cert_cbs);
   if (!pubkey) {
     OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_KEY_TYPE);
-    return 0;
+    return false;
   }
 
   return ssl_compare_public_and_private_key(pubkey.get(), privkey);
 }
 
-int ssl_cert_check_digital_signature_key_usage(const CBS *in) {
+bool ssl_cert_check_digital_signature_key_usage(const CBS *in) {
   CBS buf = *in;
 
   CBS tbs_cert, outer_extensions;
@@ -551,17 +546,17 @@
           &tbs_cert, &outer_extensions, &has_extensions,
           CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
-    return 0;
+    return false;
   }
 
   if (!has_extensions) {
-    return 1;
+    return true;
   }
 
   CBS extensions;
   if (!CBS_get_asn1(&outer_extensions, &extensions, CBS_ASN1_SEQUENCE)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
-    return 0;
+    return false;
   }
 
   while (CBS_len(&extensions) > 0) {
@@ -573,7 +568,7 @@
         !CBS_get_asn1(&extension, &contents, CBS_ASN1_OCTETSTRING) ||
         CBS_len(&extension) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
-      return 0;
+      return false;
     }
 
     static const uint8_t kKeyUsageOID[3] = {0x55, 0x1d, 0x0f};
@@ -587,26 +582,26 @@
     if (!CBS_get_asn1(&contents, &bit_string, CBS_ASN1_BITSTRING) ||
         CBS_len(&contents) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
-      return 0;
+      return false;
     }
 
     // This is the KeyUsage extension. See
     // https://tools.ietf.org/html/rfc5280#section-4.2.1.3
     if (!CBS_is_valid_asn1_bitstring(&bit_string)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT);
-      return 0;
+      return false;
     }
 
     if (!CBS_asn1_bitstring_has_bit(&bit_string, 0)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ECC_CERT_NOT_FOR_SIGNING);
-      return 0;
+      return false;
     }
 
-    return 1;
+    return true;
   }
 
   // No KeyUsage extension found.
-  return 1;
+  return true;
 }
 
 UniquePtr<STACK_OF(CRYPTO_BUFFER)> ssl_parse_client_CA_list(SSL *ssl,
@@ -666,10 +661,10 @@
   return sk_CRYPTO_BUFFER_num(names) > 0;
 }
 
-int ssl_add_client_CA_list(SSL_HANDSHAKE *hs, CBB *cbb) {
+bool ssl_add_client_CA_list(SSL_HANDSHAKE *hs, CBB *cbb) {
   CBB child, name_cbb;
   if (!CBB_add_u16_length_prefixed(cbb, &child)) {
-    return 0;
+    return false;
   }
 
   const STACK_OF(CRYPTO_BUFFER) *names = hs->config->client_CA.get();
@@ -684,21 +679,21 @@
     if (!CBB_add_u16_length_prefixed(&child, &name_cbb) ||
         !CBB_add_bytes(&name_cbb, CRYPTO_BUFFER_data(name),
                        CRYPTO_BUFFER_len(name))) {
-      return 0;
+      return false;
     }
   }
 
   return CBB_flush(cbb);
 }
 
-int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
-                               const CRYPTO_BUFFER *leaf) {
+bool ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey,
+                                const CRYPTO_BUFFER *leaf) {
   assert(ssl_protocol_version(hs->ssl) < TLS1_3_VERSION);
 
   // Check the certificate's type matches the cipher.
   if (!(hs->new_cipher->algorithm_auth & ssl_cipher_auth_mask_for_key(pkey))) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CERTIFICATE_TYPE);
-    return 0;
+    return false;
   }
 
   // Check key usages for all key types but RSA. This is needed to distinguish
@@ -711,7 +706,7 @@
     CBS leaf_cbs;
     CBS_init(&leaf_cbs, CRYPTO_BUFFER_data(leaf), CRYPTO_BUFFER_len(leaf));
     if (!ssl_cert_check_digital_signature_key_usage(&leaf_cbs)) {
-      return 0;
+      return false;
     }
   }
 
@@ -724,22 +719,22 @@
         !tls1_check_group_id(hs, group_id) ||
         EC_KEY_get_conv_form(ec_key) != POINT_CONVERSION_UNCOMPRESSED) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECC_CERT);
-      return 0;
+      return false;
     }
   }
 
-  return 1;
+  return true;
 }
 
-int ssl_on_certificate_selected(SSL_HANDSHAKE *hs) {
+bool ssl_on_certificate_selected(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   if (!ssl_has_certificate(hs->config)) {
     // Nothing to do.
-    return 1;
+    return true;
   }
 
   if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(hs)) {
-    return 0;
+    return false;
   }
 
   CBS leaf;
@@ -750,7 +745,7 @@
   return hs->local_pubkey != NULL;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/ssl_cipher.cc b/src/ssl/ssl_cipher.cc
index 5899500..0ed91d6 100644
--- a/src/ssl/ssl_cipher.cc
+++ b/src/ssl/ssl_cipher.cc
@@ -154,7 +154,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // kCiphers is an array of all supported ciphers, sorted by id.
 static constexpr SSL_CIPHER kCiphers[] = {
@@ -559,13 +559,13 @@
 bool ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead,
                              size_t *out_mac_secret_len,
                              size_t *out_fixed_iv_len, const SSL_CIPHER *cipher,
-                             uint16_t version, int is_dtls) {
+                             uint16_t version, bool is_dtls) {
   *out_aead = NULL;
   *out_mac_secret_len = 0;
   *out_fixed_iv_len = 0;
 
-  const int is_tls12 = version == TLS1_2_VERSION && !is_dtls;
-  const int is_tls13 = version == TLS1_3_VERSION && !is_dtls;
+  const bool is_tls12 = version == TLS1_2_VERSION && !is_dtls;
+  const bool is_tls13 = version == TLS1_3_VERSION && !is_dtls;
 
   if (cipher->algorithm_mac == SSL_AEAD) {
     if (cipher->algorithm_enc == SSL_AES128GCM) {
@@ -649,7 +649,7 @@
   }
 }
 
-static bool is_cipher_list_separator(char c, int is_strict) {
+static bool is_cipher_list_separator(char c, bool is_strict) {
   if (c == ':') {
     return true;
   }
@@ -1306,7 +1306,7 @@
   return ret;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/ssl_key_share.cc b/src/ssl/ssl_key_share.cc
index c7f6f88..8466eab 100644
--- a/src/ssl/ssl_key_share.cc
+++ b/src/ssl/ssl_key_share.cc
@@ -31,7 +31,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 namespace {
 
@@ -266,33 +266,33 @@
          Finish(out_secret, out_alert, peer_key);
 }
 
-int ssl_nid_to_group_id(uint16_t *out_group_id, int nid) {
+bool ssl_nid_to_group_id(uint16_t *out_group_id, int nid) {
   for (const auto &group : kNamedGroups) {
     if (group.nid == nid) {
       *out_group_id = group.group_id;
-      return 1;
+      return true;
     }
   }
-  return 0;
+  return false;
 }
 
-int ssl_name_to_group_id(uint16_t *out_group_id, const char *name, size_t len) {
+bool ssl_name_to_group_id(uint16_t *out_group_id, const char *name, size_t len) {
   for (const auto &group : kNamedGroups) {
     if (len == strlen(group.name) &&
         !strncmp(group.name, name, len)) {
       *out_group_id = group.group_id;
-      return 1;
+      return true;
     }
     if (len == strlen(group.alias) &&
         !strncmp(group.alias, name, len)) {
       *out_group_id = group.group_id;
-      return 1;
+      return true;
     }
   }
-  return 0;
+  return false;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc
index 120c276..9c16de4 100644
--- a/src/ssl/ssl_lib.cc
+++ b/src/ssl/ssl_lib.cc
@@ -162,7 +162,7 @@
 #endif
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // |SSL_R_UNKNOWN_PROTOCOL| is no longer emitted, but continue to define it
 // to avoid downstream churn.
@@ -506,7 +506,7 @@
   ssl->config->handoff = on;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
@@ -693,6 +693,7 @@
       ctx->signed_cert_timestamps_enabled;
   ssl->config->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
   ssl->config->handoff = ctx->handoff;
+  ssl->config->ignore_tls13_downgrade = ctx->ignore_tls13_downgrade;
 
   if (!ssl->method->ssl_new(ssl.get()) ||
       !ssl->ctx->x509_method->ssl_new(ssl->s3->hs.get())) {
@@ -709,7 +710,8 @@
       channel_id_enabled(false),
       retain_only_sha256_of_client_certs(false),
       handoff(false),
-      shed_handshake_config(false) {
+      shed_handshake_config(false),
+      ignore_tls13_downgrade(false) {
   assert(ssl);
 }
 
@@ -2642,6 +2644,13 @@
   ctx->ignore_tls13_downgrade = !!ignore;
 }
 
+void SSL_set_ignore_tls13_downgrade(SSL *ssl, int ignore) {
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->ignore_tls13_downgrade = !!ignore;
+}
+
 void SSL_set_shed_handshake_config(SSL *ssl, int enable) {
   if (!ssl->config) {
     return;
diff --git a/src/ssl/ssl_privkey.cc b/src/ssl/ssl_privkey.cc
index fecac39..e716c9a 100644
--- a/src/ssl/ssl_privkey.cc
+++ b/src/ssl/ssl_privkey.cc
@@ -69,28 +69,28 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
-int ssl_is_key_type_supported(int key_type) {
+bool ssl_is_key_type_supported(int key_type) {
   return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC ||
          key_type == EVP_PKEY_ED25519;
 }
 
-static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey) {
+static bool ssl_set_pkey(CERT *cert, EVP_PKEY *pkey) {
   if (!ssl_is_key_type_supported(pkey->type)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
-    return 0;
+    return false;
   }
 
   if (cert->chain != nullptr &&
       sk_CRYPTO_BUFFER_value(cert->chain.get(), 0) != nullptr &&
       // Sanity-check that the private key and the certificate match.
       !ssl_cert_check_private_key(cert, pkey)) {
-    return 0;
+    return false;
   }
 
   cert->privatekey = UpRef(pkey);
-  return 1;
+  return true;
 }
 
 typedef struct {
@@ -98,29 +98,30 @@
   int pkey_type;
   int curve;
   const EVP_MD *(*digest_func)(void);
-  char is_rsa_pss;
+  bool is_rsa_pss;
 } SSL_SIGNATURE_ALGORITHM;
 
 static const SSL_SIGNATURE_ALGORITHM kSignatureAlgorithms[] = {
-    {SSL_SIGN_RSA_PKCS1_MD5_SHA1, EVP_PKEY_RSA, NID_undef, &EVP_md5_sha1, 0},
-    {SSL_SIGN_RSA_PKCS1_SHA1, EVP_PKEY_RSA, NID_undef, &EVP_sha1, 0},
-    {SSL_SIGN_RSA_PKCS1_SHA256, EVP_PKEY_RSA, NID_undef, &EVP_sha256, 0},
-    {SSL_SIGN_RSA_PKCS1_SHA384, EVP_PKEY_RSA, NID_undef, &EVP_sha384, 0},
-    {SSL_SIGN_RSA_PKCS1_SHA512, EVP_PKEY_RSA, NID_undef, &EVP_sha512, 0},
+    {SSL_SIGN_RSA_PKCS1_MD5_SHA1, EVP_PKEY_RSA, NID_undef, &EVP_md5_sha1,
+     false},
+    {SSL_SIGN_RSA_PKCS1_SHA1, EVP_PKEY_RSA, NID_undef, &EVP_sha1, false},
+    {SSL_SIGN_RSA_PKCS1_SHA256, EVP_PKEY_RSA, NID_undef, &EVP_sha256, false},
+    {SSL_SIGN_RSA_PKCS1_SHA384, EVP_PKEY_RSA, NID_undef, &EVP_sha384, false},
+    {SSL_SIGN_RSA_PKCS1_SHA512, EVP_PKEY_RSA, NID_undef, &EVP_sha512, false},
 
-    {SSL_SIGN_RSA_PSS_RSAE_SHA256, EVP_PKEY_RSA, NID_undef, &EVP_sha256, 1},
-    {SSL_SIGN_RSA_PSS_RSAE_SHA384, EVP_PKEY_RSA, NID_undef, &EVP_sha384, 1},
-    {SSL_SIGN_RSA_PSS_RSAE_SHA512, EVP_PKEY_RSA, NID_undef, &EVP_sha512, 1},
+    {SSL_SIGN_RSA_PSS_RSAE_SHA256, EVP_PKEY_RSA, NID_undef, &EVP_sha256, true},
+    {SSL_SIGN_RSA_PSS_RSAE_SHA384, EVP_PKEY_RSA, NID_undef, &EVP_sha384, true},
+    {SSL_SIGN_RSA_PSS_RSAE_SHA512, EVP_PKEY_RSA, NID_undef, &EVP_sha512, true},
 
-    {SSL_SIGN_ECDSA_SHA1, EVP_PKEY_EC, NID_undef, &EVP_sha1, 0},
+    {SSL_SIGN_ECDSA_SHA1, EVP_PKEY_EC, NID_undef, &EVP_sha1, false},
     {SSL_SIGN_ECDSA_SECP256R1_SHA256, EVP_PKEY_EC, NID_X9_62_prime256v1,
-     &EVP_sha256, 0},
+     &EVP_sha256, false},
     {SSL_SIGN_ECDSA_SECP384R1_SHA384, EVP_PKEY_EC, NID_secp384r1, &EVP_sha384,
-     0},
+     false},
     {SSL_SIGN_ECDSA_SECP521R1_SHA512, EVP_PKEY_EC, NID_secp521r1, &EVP_sha512,
-     0},
+     false},
 
-    {SSL_SIGN_ED25519, EVP_PKEY_ED25519, NID_undef, NULL, 0},
+    {SSL_SIGN_ED25519, EVP_PKEY_ED25519, NID_undef, nullptr, false},
 };
 
 static const SSL_SIGNATURE_ALGORITHM *get_signature_algorithm(uint16_t sigalg) {
@@ -132,22 +133,22 @@
   return NULL;
 }
 
-int ssl_has_private_key(const SSL_CONFIG *cfg) {
+bool ssl_has_private_key(const SSL_CONFIG *cfg) {
   return cfg->cert->privatekey != nullptr || cfg->cert->key_method != nullptr;
 }
 
-static int pkey_supports_algorithm(const SSL *ssl, EVP_PKEY *pkey,
-                                   uint16_t sigalg) {
+static bool pkey_supports_algorithm(const SSL *ssl, EVP_PKEY *pkey,
+                                    uint16_t sigalg) {
   const SSL_SIGNATURE_ALGORITHM *alg = get_signature_algorithm(sigalg);
   if (alg == NULL ||
       EVP_PKEY_id(pkey) != alg->pkey_type) {
-    return 0;
+    return false;
   }
 
   if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     // RSA keys may only be used with RSA-PSS.
     if (alg->pkey_type == EVP_PKEY_RSA && !alg->is_rsa_pss) {
-      return 0;
+      return false;
     }
 
     // EC keys have a curve requirement.
@@ -155,18 +156,18 @@
         (alg->curve == NID_undef ||
          EC_GROUP_get_curve_name(
              EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey))) != alg->curve)) {
-      return 0;
+      return false;
     }
   }
 
-  return 1;
+  return true;
 }
 
-static int setup_ctx(SSL *ssl, EVP_MD_CTX *ctx, EVP_PKEY *pkey, uint16_t sigalg,
-                     int is_verify) {
+static bool setup_ctx(SSL *ssl, EVP_MD_CTX *ctx, EVP_PKEY *pkey,
+                      uint16_t sigalg, bool is_verify) {
   if (!pkey_supports_algorithm(ssl, pkey, sigalg)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE);
-    return 0;
+    return false;
   }
 
   const SSL_SIGNATURE_ALGORITHM *alg = get_signature_algorithm(sigalg);
@@ -174,20 +175,20 @@
   EVP_PKEY_CTX *pctx;
   if (is_verify) {
     if (!EVP_DigestVerifyInit(ctx, &pctx, digest, NULL, pkey)) {
-      return 0;
+      return false;
     }
   } else if (!EVP_DigestSignInit(ctx, &pctx, digest, NULL, pkey)) {
-    return 0;
+    return false;
   }
 
   if (alg->is_rsa_pss) {
     if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) ||
         !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* salt len = hash len */)) {
-      return 0;
+      return false;
     }
   }
 
-  return 1;
+  return true;
 }
 
 enum ssl_private_key_result_t ssl_private_key_sign(
@@ -212,7 +213,7 @@
   *out_len = max_out;
   ScopedEVP_MD_CTX ctx;
   if (!setup_ctx(ssl, ctx.get(), hs->config->cert->privatekey.get(), sigalg,
-                 0 /* sign */) ||
+                 false /* sign */) ||
       !EVP_DigestSign(ctx.get(), out, out_len, in.data(), in.size())) {
     return ssl_private_key_failure;
   }
@@ -223,7 +224,7 @@
                            uint16_t sigalg, EVP_PKEY *pkey,
                            Span<const uint8_t> in) {
   ScopedEVP_MD_CTX ctx;
-  return setup_ctx(ssl, ctx.get(), pkey, sigalg, 1 /* verify */) &&
+  return setup_ctx(ssl, ctx.get(), pkey, sigalg, true /* verify */) &&
          EVP_DigestVerify(ctx.get(), signature.data(), signature.size(),
                           in.data(), in.size());
 }
@@ -287,7 +288,7 @@
   return true;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/ssl_session.cc b/src/ssl/ssl_session.cc
index 1b0b68a..927dd1b 100644
--- a/src/ssl/ssl_session.cc
+++ b/src/ssl/ssl_session.cc
@@ -151,7 +151,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // The address of this is a magic value, a pointer to which is returned by
 // SSL_magic_pending_session_ptr(). It allows a session callback to indicate
@@ -838,7 +838,7 @@
   }
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 74c4e9e..61a47d3 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -48,12 +48,12 @@
 #include <sys/time.h>
 #endif
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <thread>
 #endif
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 namespace {
 
@@ -1966,18 +1966,18 @@
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xc0, 0x09,
       0xc0, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a,
-      0x01, 0x00, 0x00, 0x1f, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00,
-      0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
-      0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18}},
+      0x01, 0x00, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01,
+      0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00,
+      0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00}},
     {TLS1_1_VERSION,
      {0x16, 0x03, 0x01, 0x00, 0x5a, 0x01, 0x00, 0x00, 0x56, 0x03, 0x02, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xc0, 0x09,
       0xc0, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a,
-      0x01, 0x00, 0x00, 0x1f, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00,
-      0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
-      0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18}},
+      0x01, 0x00, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01,
+      0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00,
+      0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00}},
     {TLS1_2_VERSION,
      {0x16, 0x03, 0x01, 0x00, 0x82, 0x01, 0x00, 0x00, 0x7e, 0x03, 0x03, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -1985,12 +1985,12 @@
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xcc, 0xa9,
       0xcc, 0xa8, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xc0, 0x09,
       0xc0, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f,
-      0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37, 0xff, 0x01, 0x00, 0x01,
-      0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
-      0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08,
-      0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x0b, 0x00,
-      0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00,
-      0x17, 0x00, 0x18}},
+      0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37, 0x00, 0x17, 0x00, 0x00,
+      0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00,
+      0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00,
+      0x23, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08,
+      0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06,
+      0x01, 0x02, 0x01}},
     // TODO(davidben): Add a change detector for TLS 1.3 once the spec and our
     // implementation has settled enough that it won't change.
   };
@@ -2033,7 +2033,7 @@
           }
         }
         if (i + 12 >= client_hello.size()) {
-          printf("}}");
+          printf("}},");
         }
         printf("\n");
       }
@@ -4281,9 +4281,24 @@
                                       server_ctx_.get()));
 }
 
+// Test that ticket-based sessions on the client get fake session IDs.
+TEST_P(SSLVersionTest, FakeIDsForTickets) {
+  SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
+  SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH);
+
+  bssl::UniquePtr<SSL_SESSION> session =
+      CreateClientSession(client_ctx_.get(), server_ctx_.get());
+  ASSERT_TRUE(session);
+
+  EXPECT_TRUE(SSL_SESSION_has_ticket(session.get()));
+  unsigned session_id_length;
+  SSL_SESSION_get_id(session.get(), &session_id_length);
+  EXPECT_NE(session_id_length, 0u);
+}
+
 // These tests test multi-threaded behavior. They are intended to run with
 // ThreadSanitizer.
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 TEST_P(SSLVersionTest, SessionCacheThreads) {
   SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET);
   SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
@@ -4428,4 +4443,4 @@
 }
 
 }  // namespace
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/ssl_transcript.cc b/src/ssl/ssl_transcript.cc
index 24b86bf..8bb513d 100644
--- a/src/ssl/ssl_transcript.cc
+++ b/src/ssl/ssl_transcript.cc
@@ -141,7 +141,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 SSLTranscript::SSLTranscript() {}
 
@@ -261,4 +261,4 @@
   return true;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/ssl_versions.cc b/src/ssl/ssl_versions.cc
index 212c3ac..911fb7e 100644
--- a/src/ssl/ssl_versions.cc
+++ b/src/ssl/ssl_versions.cc
@@ -23,7 +23,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 bool ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) {
   switch (version) {
@@ -360,7 +360,7 @@
   return version == TLS1_3_DRAFT28_VERSION || version == TLS1_3_VERSION;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/ssl_x509.cc b/src/ssl/ssl_x509.cc
index ef09589..ec203b2 100644
--- a/src/ssl/ssl_x509.cc
+++ b/src/ssl/ssl_x509.cc
@@ -155,7 +155,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // check_ssl_x509_method asserts that |ssl| has the X509-based method
 // installed. Calling an X509-based method on an |ssl| with a different method
@@ -506,7 +506,7 @@
   ssl_crypto_x509_ssl_ctx_flush_cached_client_CA,
 };
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
@@ -999,17 +999,25 @@
   return 1;
 }
 
-static SSL_SESSION *ssl_session_new_with_crypto_x509(void) {
-  return ssl_session_new(&ssl_crypto_x509_method).release();
-}
-
 SSL_SESSION *d2i_SSL_SESSION_bio(BIO *bio, SSL_SESSION **out) {
-  return ASN1_d2i_bio_of(SSL_SESSION, ssl_session_new_with_crypto_x509,
-                         d2i_SSL_SESSION, bio, out);
+  uint8_t *data;
+  size_t len;
+  if (!BIO_read_asn1(bio, &data, &len, 1024 * 1024)) {
+    return 0;
+  }
+  bssl::UniquePtr<uint8_t> free_data(data);
+  const uint8_t *ptr = data;
+  return d2i_SSL_SESSION(out, &ptr, static_cast<long>(len));
 }
 
 int i2d_SSL_SESSION_bio(BIO *bio, const SSL_SESSION *session) {
-  return ASN1_i2d_bio_of(SSL_SESSION, i2d_SSL_SESSION, bio, session);
+  uint8_t *data;
+  size_t len;
+  if (!SSL_SESSION_to_bytes(session, &data, &len)) {
+    return 0;
+  }
+  bssl::UniquePtr<uint8_t> free_data(data);
+  return BIO_write_all(bio, data, len);
 }
 
 IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION)
diff --git a/src/ssl/t1_enc.cc b/src/ssl/t1_enc.cc
index 93170b9..c6b2844 100644
--- a/src/ssl/t1_enc.cc
+++ b/src/ssl/t1_enc.cc
@@ -153,7 +153,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 bool tls1_prf(const EVP_MD *digest, Span<uint8_t> out,
               Span<const uint8_t> secret, Span<const char> label,
@@ -277,7 +277,7 @@
   return SSL3_MASTER_SECRET_SIZE;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 32ea2d4..678e4a3 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -129,7 +129,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 static bool ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs);
 
@@ -2775,14 +2775,6 @@
 // kExtensions contains all the supported extensions.
 static const struct tls_extension kExtensions[] = {
   {
-    TLSEXT_TYPE_renegotiate,
-    NULL,
-    ext_ri_add_clienthello,
-    ext_ri_parse_serverhello,
-    ext_ri_parse_clienthello,
-    ext_ri_add_serverhello,
-  },
-  {
     TLSEXT_TYPE_server_name,
     NULL,
     ext_sni_add_clienthello,
@@ -2799,6 +2791,30 @@
     ext_ems_add_serverhello,
   },
   {
+    TLSEXT_TYPE_renegotiate,
+    NULL,
+    ext_ri_add_clienthello,
+    ext_ri_parse_serverhello,
+    ext_ri_parse_clienthello,
+    ext_ri_add_serverhello,
+  },
+  {
+    TLSEXT_TYPE_supported_groups,
+    NULL,
+    ext_supported_groups_add_clienthello,
+    ext_supported_groups_parse_serverhello,
+    ext_supported_groups_parse_clienthello,
+    dont_add_serverhello,
+  },
+  {
+    TLSEXT_TYPE_ec_point_formats,
+    NULL,
+    ext_ec_point_add_clienthello,
+    ext_ec_point_parse_serverhello,
+    ext_ec_point_parse_clienthello,
+    ext_ec_point_add_serverhello,
+  },
+  {
     TLSEXT_TYPE_session_ticket,
     NULL,
     ext_ticket_add_clienthello,
@@ -2808,6 +2824,23 @@
     ext_ticket_add_serverhello,
   },
   {
+    TLSEXT_TYPE_application_layer_protocol_negotiation,
+    NULL,
+    ext_alpn_add_clienthello,
+    ext_alpn_parse_serverhello,
+    // ALPN is negotiated late in |ssl_negotiate_alpn|.
+    ignore_parse_clienthello,
+    ext_alpn_add_serverhello,
+  },
+  {
+    TLSEXT_TYPE_status_request,
+    NULL,
+    ext_ocsp_add_clienthello,
+    ext_ocsp_parse_serverhello,
+    ext_ocsp_parse_clienthello,
+    ext_ocsp_add_serverhello,
+  },
+  {
     TLSEXT_TYPE_signature_algorithms,
     NULL,
     ext_sigalgs_add_clienthello,
@@ -2824,14 +2857,6 @@
     dont_add_serverhello,
   },
   {
-    TLSEXT_TYPE_status_request,
-    NULL,
-    ext_ocsp_add_clienthello,
-    ext_ocsp_parse_serverhello,
-    ext_ocsp_parse_clienthello,
-    ext_ocsp_add_serverhello,
-  },
-  {
     TLSEXT_TYPE_next_proto_neg,
     NULL,
     ext_npn_add_clienthello,
@@ -2848,15 +2873,6 @@
     ext_sct_add_serverhello,
   },
   {
-    TLSEXT_TYPE_application_layer_protocol_negotiation,
-    NULL,
-    ext_alpn_add_clienthello,
-    ext_alpn_parse_serverhello,
-    // ALPN is negotiated late in |ssl_negotiate_alpn|.
-    ignore_parse_clienthello,
-    ext_alpn_add_serverhello,
-  },
-  {
     TLSEXT_TYPE_channel_id,
     ext_channel_id_init,
     ext_channel_id_add_clienthello,
@@ -2873,14 +2889,6 @@
     ext_srtp_add_serverhello,
   },
   {
-    TLSEXT_TYPE_ec_point_formats,
-    NULL,
-    ext_ec_point_add_clienthello,
-    ext_ec_point_parse_serverhello,
-    ext_ec_point_parse_clienthello,
-    ext_ec_point_add_serverhello,
-  },
-  {
     TLSEXT_TYPE_key_share,
     NULL,
     ext_key_share_add_clienthello,
@@ -2929,14 +2937,6 @@
     ext_quic_transport_params_add_serverhello,
   },
   {
-    TLSEXT_TYPE_supported_groups,
-    NULL,
-    ext_supported_groups_add_clienthello,
-    ext_supported_groups_parse_serverhello,
-    ext_supported_groups_parse_clienthello,
-    dont_add_serverhello,
-  },
-  {
     TLSEXT_TYPE_token_binding,
     NULL,
     ext_token_binding_add_clienthello,
@@ -3836,7 +3836,7 @@
   return true;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/test/CMakeLists.txt b/src/ssl/test/CMakeLists.txt
index 425b43b..d86464c 100644
--- a/src/ssl/test/CMakeLists.txt
+++ b/src/ssl/test/CMakeLists.txt
@@ -14,6 +14,8 @@
   $<TARGET_OBJECTS:test_support>
 )
 
+add_dependencies(bssl_shim global_target)
+
 target_link_libraries(bssl_shim ssl crypto)
 
 if(UNIX AND NOT APPLE AND NOT ANDROID)
@@ -31,6 +33,8 @@
     $<TARGET_OBJECTS:test_support>
   )
 
+  add_dependencies(handshaker global_target)
+
   target_link_libraries(handshaker ssl crypto)
 else()
   # Declare a dummy target for run_tests to depend on.
diff --git a/src/ssl/test/handshake_util.cc b/src/ssl/test/handshake_util.cc
index f839653..a36b41a 100644
--- a/src/ssl/test/handshake_util.cc
+++ b/src/ssl/test/handshake_util.cc
@@ -349,7 +349,7 @@
   // it to -1.
   pid_t handshaker_pid = -1;
   int ret = posix_spawn(&handshaker_pid, args[0], &actions, nullptr,
-                        args.data(), nullptr);
+                        args.data(), environ);
   if (posix_spawn_file_actions_destroy(&actions) != 0 ||
       ret != 0) {
     return false;
diff --git a/src/ssl/test/runner/chacha20_poly1305.go b/src/ssl/test/runner/chacha20_poly1305.go
index 866f724..446fb55 100644
--- a/src/ssl/test/runner/chacha20_poly1305.go
+++ b/src/ssl/test/runner/chacha20_poly1305.go
@@ -20,7 +20,7 @@
 	"encoding/binary"
 	"errors"
 
-	"./poly1305"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/poly1305"
 )
 
 // See RFC 7539.
diff --git a/src/ssl/test/runner/ed25519/ed25519.go b/src/ssl/test/runner/ed25519/ed25519.go
index dfef0ff..c8fcc3a 100644
--- a/src/ssl/test/runner/ed25519/ed25519.go
+++ b/src/ssl/test/runner/ed25519/ed25519.go
@@ -3,25 +3,28 @@
 // license that can be found in the LICENSE file.
 
 // Package ed25519 implements the Ed25519 signature algorithm. See
-// http://ed25519.cr.yp.to/.
+// https://ed25519.cr.yp.to/.
 //
 // These functions are also compatible with the “Ed25519” function defined in
-// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05.
+// RFC 8032. However, unlike RFC 8032's formulation, this package's private key
+// representation includes a public key suffix to make multiple signing
+// operations with the same key more efficient. This package refers to the RFC
+// 8032 private key as the “seed”.
 package ed25519
 
 // This code is a port of the public domain, “ref10” implementation of ed25519
 // from SUPERCOP.
 
 import (
+	"bytes"
 	"crypto"
 	cryptorand "crypto/rand"
 	"crypto/sha512"
-	"crypto/subtle"
 	"errors"
 	"io"
 	"strconv"
 
-	"./internal/edwards25519"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/ed25519/internal/edwards25519"
 )
 
 const (
@@ -31,6 +34,8 @@
 	PrivateKeySize = 64
 	// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
 	SignatureSize = 64
+	// SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032.
+	SeedSize = 32
 )
 
 // PublicKey is the type of Ed25519 public keys.
@@ -46,6 +51,15 @@
 	return PublicKey(publicKey)
 }
 
+// Seed returns the private key seed corresponding to priv. It is provided for
+// interoperability with RFC 8032. RFC 8032's private keys correspond to seeds
+// in this package.
+func (priv PrivateKey) Seed() []byte {
+	seed := make([]byte, SeedSize)
+	copy(seed, priv[:32])
+	return seed
+}
+
 // Sign signs the given message with priv.
 // Ed25519 performs two passes over messages to be signed and therefore cannot
 // handle pre-hashed messages. Thus opts.HashFunc() must return zero to
@@ -61,26 +75,30 @@
 
 // GenerateKey generates a public/private key pair using entropy from rand.
 // If rand is nil, crypto/rand.Reader will be used.
-func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
+func GenerateKey(rand io.Reader) (PublicKey, PrivateKey, error) {
 	if rand == nil {
 		rand = cryptorand.Reader
 	}
 
-	var seed [32]byte
-	_, err = io.ReadFull(rand, seed[:])
-	if err != nil {
+	seed := make([]byte, SeedSize)
+	if _, err := io.ReadFull(rand, seed); err != nil {
 		return nil, nil, err
 	}
 
-	publicKey, privateKey = NewKeyPairFromSeed(seed[:])
+	privateKey := NewKeyFromSeed(seed)
+	publicKey := make([]byte, PublicKeySize)
+	copy(publicKey, privateKey[32:])
+
 	return publicKey, privateKey, nil
 }
 
-// NewKeyPairFromSeed calculates a public and private key from a 32-byte
-// Ed25519 seed.
-func NewKeyPairFromSeed(seed []byte) (publicKey PublicKey, privateKey PrivateKey) {
-	if len(seed) != 32 {
-		panic("Invalid seed length.")
+// NewKeyFromSeed calculates a private key from a seed. It will panic if
+// len(seed) is not SeedSize. This function is provided for interoperability
+// with RFC 8032. RFC 8032's private keys correspond to seeds in this
+// package.
+func NewKeyFromSeed(seed []byte) PrivateKey {
+	if l := len(seed); l != SeedSize {
+		panic("ed25519: bad seed length: " + strconv.Itoa(l))
 	}
 
 	digest := sha512.Sum512(seed)
@@ -88,9 +106,6 @@
 	digest[31] &= 127
 	digest[31] |= 64
 
-	privateKey = make([]byte, PrivateKeySize)
-	publicKey = make([]byte, PublicKeySize)
-
 	var A edwards25519.ExtendedGroupElement
 	var hBytes [32]byte
 	copy(hBytes[:], digest[:])
@@ -98,11 +113,11 @@
 	var publicKeyBytes [32]byte
 	A.ToBytes(&publicKeyBytes)
 
-	copy(privateKey, seed[:])
+	privateKey := make([]byte, PrivateKeySize)
+	copy(privateKey, seed)
 	copy(privateKey[32:], publicKeyBytes[:])
-	copy(publicKey, publicKeyBytes[:])
 
-	return publicKey, privateKey
+	return privateKey
 }
 
 // Sign signs the message with privateKey and returns a signature. It will
@@ -185,11 +200,18 @@
 	edwards25519.ScReduce(&hReduced, &digest)
 
 	var R edwards25519.ProjectiveGroupElement
-	var b [32]byte
-	copy(b[:], sig[32:])
-	edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
+	var s [32]byte
+	copy(s[:], sig[32:])
+
+	// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
+	// the range [0, order) in order to prevent signature malleability.
+	if !edwards25519.ScMinimal(&s) {
+		return false
+	}
+
+	edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
 
 	var checkR [32]byte
 	R.ToBytes(&checkR)
-	return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
+	return bytes.Equal(sig[:32], checkR[:])
 }
diff --git a/src/ssl/test/runner/ed25519/ed25519_test.go b/src/ssl/test/runner/ed25519/ed25519_test.go
index 02c9861..bb6d122 100644
--- a/src/ssl/test/runner/ed25519/ed25519_test.go
+++ b/src/ssl/test/runner/ed25519/ed25519_test.go
@@ -15,7 +15,7 @@
 	"strings"
 	"testing"
 
-	"./internal/edwards25519"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/ed25519/internal/edwards25519"
 )
 
 type zeroReader struct{}
@@ -91,7 +91,7 @@
 
 func TestGolden(t *testing.T) {
 	// sign.input.gz is a selection of test cases from
-	// http://ed25519.cr.yp.to/python/sign.input
+	// https://ed25519.cr.yp.to/python/sign.input
 	testDataZ, err := os.Open("testdata/sign.input.gz")
 	if err != nil {
 		t.Fatal(err)
@@ -139,6 +139,19 @@
 		if !Verify(pubKey, msg, sig2) {
 			t.Errorf("signature failed to verify on line %d", lineNo)
 		}
+
+		priv2 := NewKeyFromSeed(priv[:32])
+		if !bytes.Equal(priv[:], priv2) {
+			t.Errorf("recreating key pair gave different private key on line %d: %x vs %x", lineNo, priv[:], priv2)
+		}
+
+		if pubKey2 := priv2.Public().(PublicKey); !bytes.Equal(pubKey, pubKey2) {
+			t.Errorf("recreating key pair gave different public key on line %d: %x vs %x", lineNo, pubKey, pubKey2)
+		}
+
+		if seed := priv2.Seed(); !bytes.Equal(priv[:32], seed) {
+			t.Errorf("recreating key pair gave different seed on line %d: %x vs %x", lineNo, priv[:32], seed)
+		}
 	}
 
 	if err := scanner.Err(); err != nil {
@@ -146,6 +159,30 @@
 	}
 }
 
+func TestMalleability(t *testing.T) {
+	// https://tools.ietf.org/html/rfc8032#section-5.1.7 adds an additional test
+	// that s be in [0, order). This prevents someone from adding a multiple of
+	// order to s and obtaining a second valid signature for the same message.
+	msg := []byte{0x54, 0x65, 0x73, 0x74}
+	sig := []byte{
+		0x7c, 0x38, 0xe0, 0x26, 0xf2, 0x9e, 0x14, 0xaa, 0xbd, 0x05, 0x9a,
+		0x0f, 0x2d, 0xb8, 0xb0, 0xcd, 0x78, 0x30, 0x40, 0x60, 0x9a, 0x8b,
+		0xe6, 0x84, 0xdb, 0x12, 0xf8, 0x2a, 0x27, 0x77, 0x4a, 0xb0, 0x67,
+		0x65, 0x4b, 0xce, 0x38, 0x32, 0xc2, 0xd7, 0x6f, 0x8f, 0x6f, 0x5d,
+		0xaf, 0xc0, 0x8d, 0x93, 0x39, 0xd4, 0xee, 0xf6, 0x76, 0x57, 0x33,
+		0x36, 0xa5, 0xc5, 0x1e, 0xb6, 0xf9, 0x46, 0xb3, 0x1d,
+	}
+	publicKey := []byte{
+		0x7d, 0x4d, 0x0e, 0x7f, 0x61, 0x53, 0xa6, 0x9b, 0x62, 0x42, 0xb5,
+		0x22, 0xab, 0xbe, 0xe6, 0x85, 0xfd, 0xa4, 0x42, 0x0f, 0x88, 0x34,
+		0xb1, 0x08, 0xc3, 0xbd, 0xae, 0x36, 0x9e, 0xf5, 0x49, 0xfa,
+	}
+
+	if Verify(publicKey, msg, sig) {
+		t.Fatal("non-canonical signature accepted")
+	}
+}
+
 func BenchmarkKeyGeneration(b *testing.B) {
 	var zero zeroReader
 	for i := 0; i < b.N; i++ {
diff --git a/src/ssl/test/runner/ed25519/internal/edwards25519/edwards25519.go b/src/ssl/test/runner/ed25519/internal/edwards25519/edwards25519.go
index 5f8b994..fd03c25 100644
--- a/src/ssl/test/runner/ed25519/internal/edwards25519/edwards25519.go
+++ b/src/ssl/test/runner/ed25519/internal/edwards25519/edwards25519.go
@@ -4,6 +4,8 @@
 
 package edwards25519
 
+import "encoding/binary"
+
 // This code is a port of the public domain, “ref10” implementation of ed25519
 // from SUPERCOP.
 
@@ -1769,3 +1771,23 @@
 	out[30] = byte(s11 >> 9)
 	out[31] = byte(s11 >> 17)
 }
+
+// order is the order of Curve25519 in little-endian form.
+var order = [4]uint64{0x5812631a5cf5d3ed, 0x14def9dea2f79cd6, 0, 0x1000000000000000}
+
+// ScMinimal returns true if the given scalar is less than the order of the
+// curve.
+func ScMinimal(scalar *[32]byte) bool {
+	for i := 3; ; i-- {
+		v := binary.LittleEndian.Uint64(scalar[i*8:])
+		if v > order[i] {
+			return false
+		} else if v < order[i] {
+			break
+		} else if i == 0 {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 49e947d..4dfa469 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -19,7 +19,7 @@
 	"net"
 	"time"
 
-	"./ed25519"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/ed25519"
 )
 
 type clientHandshakeState struct {
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index ca7a7d3..4950a4f 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -18,7 +18,7 @@
 	"math/big"
 	"time"
 
-	"./ed25519"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/ed25519"
 )
 
 // serverHandshakeState contains details of a server handshake in progress.
diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go
index 5a30469..791325c 100644
--- a/src/ssl/test/runner/key_agreement.go
+++ b/src/ssl/test/runner/key_agreement.go
@@ -15,8 +15,8 @@
 	"io"
 	"math/big"
 
-	"./curve25519"
-	"./ed25519"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/curve25519"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/ed25519"
 )
 
 type keyType int
diff --git a/src/ssl/test/runner/poly1305/poly1305.go b/src/ssl/test/runner/poly1305/poly1305.go
index 4a5f826..f562fa5 100644
--- a/src/ssl/test/runner/poly1305/poly1305.go
+++ b/src/ssl/test/runner/poly1305/poly1305.go
@@ -3,7 +3,8 @@
 // license that can be found in the LICENSE file.
 
 /*
-Package poly1305 implements Poly1305 one-time message authentication code as specified in http://cr.yp.to/mac/poly1305-20050329.pdf.
+Package poly1305 implements Poly1305 one-time message authentication code as
+specified in https://cr.yp.to/mac/poly1305-20050329.pdf.
 
 Poly1305 is a fast, one-time authentication function. It is infeasible for an
 attacker to generate an authenticator for a message without the key. However, a
diff --git a/src/ssl/test/runner/poly1305/poly1305_test.go b/src/ssl/test/runner/poly1305/poly1305_test.go
index 017027f..256bdbb 100644
--- a/src/ssl/test/runner/poly1305/poly1305_test.go
+++ b/src/ssl/test/runner/poly1305/poly1305_test.go
@@ -5,7 +5,6 @@
 package poly1305
 
 import (
-	"bytes"
 	"encoding/hex"
 	"flag"
 	"testing"
@@ -14,80 +13,51 @@
 
 var stressFlag = flag.Bool("stress", false, "run slow stress tests")
 
-var testData = []struct {
-	in, k, correct []byte
-}{
-	{
-		[]byte("Hello world!"),
-		[]byte("this is 32-byte key for Poly1305"),
-		[]byte{0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0},
-	},
-	{
-		make([]byte, 32),
-		[]byte("this is 32-byte key for Poly1305"),
-		[]byte{0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07},
-	},
-	{
-		make([]byte, 2007),
-		[]byte("this is 32-byte key for Poly1305"),
-		[]byte{0xda, 0x84, 0xbc, 0xab, 0x02, 0x67, 0x6c, 0x38, 0xcd, 0xb0, 0x15, 0x60, 0x42, 0x74, 0xc2, 0xaa},
-	},
-	{
-		make([]byte, 2007),
-		make([]byte, 32),
-		make([]byte, 16),
-	},
-	{
-		// This test triggers an edge-case. See https://go-review.googlesource.com/#/c/30101/.
-		[]byte{0x81, 0xd8, 0xb2, 0xe4, 0x6a, 0x25, 0x21, 0x3b, 0x58, 0xfe, 0xe4, 0x21, 0x3a, 0x2a, 0x28, 0xe9, 0x21, 0xc1, 0x2a, 0x96, 0x32, 0x51, 0x6d, 0x3b, 0x73, 0x27, 0x27, 0x27, 0xbe, 0xcf, 0x21, 0x29},
-		[]byte{0x3b, 0x3a, 0x29, 0xe9, 0x3b, 0x21, 0x3a, 0x5c, 0x5c, 0x3b, 0x3b, 0x05, 0x3a, 0x3a, 0x8c, 0x0d},
-		[]byte{0x6d, 0xc1, 0x8b, 0x8c, 0x34, 0x4c, 0xd7, 0x99, 0x27, 0x11, 0x8b, 0xbe, 0x84, 0xb7, 0xf3, 0x14},
-	},
-	{
-		// This test generates a result of (2^130-1) % (2^130-5).
-		[]byte{
-			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		},
-		[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-		[]byte{4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	},
-	{
-		// This test generates a result of (2^130-6) % (2^130-5).
-		[]byte{
-			0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		},
-		[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-		[]byte{0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
-	},
-	{
-		// This test generates a result of (2^130-5) % (2^130-5).
-		[]byte{
-			0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		},
-		[]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-		[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
-	},
+type test struct {
+	in  string
+	key string
+	tag string
 }
 
-func testSum(t *testing.T, unaligned bool) {
-	var out [16]byte
-	var key [32]byte
+func (t *test) Input() []byte {
+	in, err := hex.DecodeString(t.in)
+	if err != nil {
+		panic(err)
+	}
+	return in
+}
 
+func (t *test) Key() [32]byte {
+	buf, err := hex.DecodeString(t.key)
+	if err != nil {
+		panic(err)
+	}
+	var key [32]byte
+	copy(key[:], buf[:32])
+	return key
+}
+
+func (t *test) Tag() [16]byte {
+	buf, err := hex.DecodeString(t.tag)
+	if err != nil {
+		panic(err)
+	}
+	var tag [16]byte
+	copy(tag[:], buf[:16])
+	return tag
+}
+
+func testSum(t *testing.T, unaligned bool, sumImpl func(tag *[TagSize]byte, msg []byte, key *[32]byte)) {
+	var tag [16]byte
 	for i, v := range testData {
-		in := v.in
+		in := v.Input()
 		if unaligned {
 			in = unalignBytes(in)
 		}
-		copy(key[:], v.k)
-		Sum(&out, in, &key)
-		if !bytes.Equal(out[:], v.correct) {
-			t.Errorf("%d: expected %x, got %x", i, v.correct, out[:])
+		key := v.Key()
+		sumImpl(&tag, in, &key)
+		if tag != v.Tag() {
+			t.Errorf("%d: expected %x, got %x", i, v.Tag(), tag[:])
 		}
 	}
 }
@@ -125,8 +95,10 @@
 	}
 }
 
-func TestSum(t *testing.T)          { testSum(t, false) }
-func TestSumUnaligned(t *testing.T) { testSum(t, true) }
+func TestSum(t *testing.T)                 { testSum(t, false, Sum) }
+func TestSumUnaligned(t *testing.T)        { testSum(t, true, Sum) }
+func TestSumGeneric(t *testing.T)          { testSum(t, false, sumGeneric) }
+func TestSumGenericUnaligned(t *testing.T) { testSum(t, true, sumGeneric) }
 
 func benchmark(b *testing.B, size int, unaligned bool) {
 	var out [16]byte
@@ -146,6 +118,7 @@
 func Benchmark1K(b *testing.B)          { benchmark(b, 1024, false) }
 func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) }
 func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) }
+func Benchmark2M(b *testing.B)          { benchmark(b, 2097152, true) }
 
 func unalignBytes(in []byte) []byte {
 	out := make([]byte, len(in)+1)
diff --git a/src/ssl/test/runner/poly1305/sum_noasm.go b/src/ssl/test/runner/poly1305/sum_noasm.go
new file mode 100644
index 0000000..751eec5
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/sum_noasm.go
@@ -0,0 +1,14 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,!go1.11 !arm,!amd64,!s390x gccgo appengine nacl
+
+package poly1305
+
+// Sum generates an authenticator for msg using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) {
+	sumGeneric(out, msg, key)
+}
diff --git a/src/ssl/test/runner/poly1305/sum_ref.go b/src/ssl/test/runner/poly1305/sum_ref.go
index b2805a5..c4d59bd 100644
--- a/src/ssl/test/runner/poly1305/sum_ref.go
+++ b/src/ssl/test/runner/poly1305/sum_ref.go
@@ -2,16 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build !amd64,!arm gccgo appengine nacl
-
 package poly1305
 
 import "encoding/binary"
 
-// Sum generates an authenticator for msg using a one-time key and puts the
-// 16-byte result into out. Authenticating two different messages with the same
-// key allows an attacker to forge messages at will.
-func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) {
+// sumGeneric generates an authenticator for msg using a one-time key and
+// puts the 16-byte result into out. This is the generic implementation of
+// Sum and should be called if no assembly implementation is available.
+func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) {
 	var (
 		h0, h1, h2, h3, h4 uint32 // the hash accumulators
 		r0, r1, r2, r3, r4 uint64 // the r part of the key
diff --git a/src/ssl/test/runner/poly1305/sum_s390x.go b/src/ssl/test/runner/poly1305/sum_s390x.go
new file mode 100644
index 0000000..7a266ce
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/sum_s390x.go
@@ -0,0 +1,49 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,go1.11,!gccgo,!appengine
+
+package poly1305
+
+// hasVectorFacility reports whether the machine supports
+// the vector facility (vx).
+func hasVectorFacility() bool
+
+// hasVMSLFacility reports whether the machine supports
+// Vector Multiply Sum Logical (VMSL).
+func hasVMSLFacility() bool
+
+var hasVX = hasVectorFacility()
+var hasVMSL = hasVMSLFacility()
+
+// poly1305vx is an assembly implementation of Poly1305 that uses vector
+// instructions. It must only be called if the vector facility (vx) is
+// available.
+//go:noescape
+func poly1305vx(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
+
+// poly1305vmsl is an assembly implementation of Poly1305 that uses vector
+// instructions, including VMSL. It must only be called if the vector facility (vx) is
+// available and if VMSL is supported.
+//go:noescape
+func poly1305vmsl(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
+
+// Sum generates an authenticator for m using a one-time key and puts the
+// 16-byte result into out. Authenticating two different messages with the same
+// key allows an attacker to forge messages at will.
+func Sum(out *[16]byte, m []byte, key *[32]byte) {
+	if hasVX {
+		var mPtr *byte
+		if len(m) > 0 {
+			mPtr = &m[0]
+		}
+		if hasVMSL && len(m) > 256 {
+			poly1305vmsl(out, mPtr, uint64(len(m)), key)
+		} else {
+			poly1305vx(out, mPtr, uint64(len(m)), key)
+		}
+	} else {
+		sumGeneric(out, m, key)
+	}
+}
diff --git a/src/ssl/test/runner/poly1305/sum_s390x.s b/src/ssl/test/runner/poly1305/sum_s390x.s
new file mode 100644
index 0000000..356c07a
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/sum_s390x.s
@@ -0,0 +1,400 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,go1.11,!gccgo,!appengine
+
+#include "textflag.h"
+
+// Implementation of Poly1305 using the vector facility (vx).
+
+// constants
+#define MOD26 V0
+#define EX0   V1
+#define EX1   V2
+#define EX2   V3
+
+// temporaries
+#define T_0 V4
+#define T_1 V5
+#define T_2 V6
+#define T_3 V7
+#define T_4 V8
+
+// key (r)
+#define R_0  V9
+#define R_1  V10
+#define R_2  V11
+#define R_3  V12
+#define R_4  V13
+#define R5_1 V14
+#define R5_2 V15
+#define R5_3 V16
+#define R5_4 V17
+#define RSAVE_0 R5
+#define RSAVE_1 R6
+#define RSAVE_2 R7
+#define RSAVE_3 R8
+#define RSAVE_4 R9
+#define R5SAVE_1 V28
+#define R5SAVE_2 V29
+#define R5SAVE_3 V30
+#define R5SAVE_4 V31
+
+// message block
+#define F_0 V18
+#define F_1 V19
+#define F_2 V20
+#define F_3 V21
+#define F_4 V22
+
+// accumulator
+#define H_0 V23
+#define H_1 V24
+#define H_2 V25
+#define H_3 V26
+#define H_4 V27
+
+GLOBL ·keyMask<>(SB), RODATA, $16
+DATA ·keyMask<>+0(SB)/8, $0xffffff0ffcffff0f
+DATA ·keyMask<>+8(SB)/8, $0xfcffff0ffcffff0f
+
+GLOBL ·bswapMask<>(SB), RODATA, $16
+DATA ·bswapMask<>+0(SB)/8, $0x0f0e0d0c0b0a0908
+DATA ·bswapMask<>+8(SB)/8, $0x0706050403020100
+
+GLOBL ·constants<>(SB), RODATA, $64
+// MOD26
+DATA ·constants<>+0(SB)/8, $0x3ffffff
+DATA ·constants<>+8(SB)/8, $0x3ffffff
+// EX0
+DATA ·constants<>+16(SB)/8, $0x0006050403020100
+DATA ·constants<>+24(SB)/8, $0x1016151413121110
+// EX1
+DATA ·constants<>+32(SB)/8, $0x060c0b0a09080706
+DATA ·constants<>+40(SB)/8, $0x161c1b1a19181716
+// EX2
+DATA ·constants<>+48(SB)/8, $0x0d0d0d0d0d0f0e0d
+DATA ·constants<>+56(SB)/8, $0x1d1d1d1d1d1f1e1d
+
+// h = (f*g) % (2**130-5) [partial reduction]
+#define MULTIPLY(f0, f1, f2, f3, f4, g0, g1, g2, g3, g4, g51, g52, g53, g54, h0, h1, h2, h3, h4) \
+	VMLOF  f0, g0, h0        \
+	VMLOF  f0, g1, h1        \
+	VMLOF  f0, g2, h2        \
+	VMLOF  f0, g3, h3        \
+	VMLOF  f0, g4, h4        \
+	VMLOF  f1, g54, T_0      \
+	VMLOF  f1, g0, T_1       \
+	VMLOF  f1, g1, T_2       \
+	VMLOF  f1, g2, T_3       \
+	VMLOF  f1, g3, T_4       \
+	VMALOF f2, g53, h0, h0   \
+	VMALOF f2, g54, h1, h1   \
+	VMALOF f2, g0, h2, h2    \
+	VMALOF f2, g1, h3, h3    \
+	VMALOF f2, g2, h4, h4    \
+	VMALOF f3, g52, T_0, T_0 \
+	VMALOF f3, g53, T_1, T_1 \
+	VMALOF f3, g54, T_2, T_2 \
+	VMALOF f3, g0, T_3, T_3  \
+	VMALOF f3, g1, T_4, T_4  \
+	VMALOF f4, g51, h0, h0   \
+	VMALOF f4, g52, h1, h1   \
+	VMALOF f4, g53, h2, h2   \
+	VMALOF f4, g54, h3, h3   \
+	VMALOF f4, g0, h4, h4    \
+	VAG    T_0, h0, h0       \
+	VAG    T_1, h1, h1       \
+	VAG    T_2, h2, h2       \
+	VAG    T_3, h3, h3       \
+	VAG    T_4, h4, h4
+
+// carry h0->h1 h3->h4, h1->h2 h4->h0, h0->h1 h2->h3, h3->h4
+#define REDUCE(h0, h1, h2, h3, h4) \
+	VESRLG $26, h0, T_0  \
+	VESRLG $26, h3, T_1  \
+	VN     MOD26, h0, h0 \
+	VN     MOD26, h3, h3 \
+	VAG    T_0, h1, h1   \
+	VAG    T_1, h4, h4   \
+	VESRLG $26, h1, T_2  \
+	VESRLG $26, h4, T_3  \
+	VN     MOD26, h1, h1 \
+	VN     MOD26, h4, h4 \
+	VESLG  $2, T_3, T_4  \
+	VAG    T_3, T_4, T_4 \
+	VAG    T_2, h2, h2   \
+	VAG    T_4, h0, h0   \
+	VESRLG $26, h2, T_0  \
+	VESRLG $26, h0, T_1  \
+	VN     MOD26, h2, h2 \
+	VN     MOD26, h0, h0 \
+	VAG    T_0, h3, h3   \
+	VAG    T_1, h1, h1   \
+	VESRLG $26, h3, T_2  \
+	VN     MOD26, h3, h3 \
+	VAG    T_2, h4, h4
+
+// expand in0 into d[0] and in1 into d[1]
+#define EXPAND(in0, in1, d0, d1, d2, d3, d4) \
+	VGBM   $0x0707, d1       \ // d1=tmp
+	VPERM  in0, in1, EX2, d4 \
+	VPERM  in0, in1, EX0, d0 \
+	VPERM  in0, in1, EX1, d2 \
+	VN     d1, d4, d4        \
+	VESRLG $26, d0, d1       \
+	VESRLG $30, d2, d3       \
+	VESRLG $4, d2, d2        \
+	VN     MOD26, d0, d0     \
+	VN     MOD26, d1, d1     \
+	VN     MOD26, d2, d2     \
+	VN     MOD26, d3, d3
+
+// pack h4:h0 into h1:h0 (no carry)
+#define PACK(h0, h1, h2, h3, h4) \
+	VESLG $26, h1, h1  \
+	VESLG $26, h3, h3  \
+	VO    h0, h1, h0   \
+	VO    h2, h3, h2   \
+	VESLG $4, h2, h2   \
+	VLEIB $7, $48, h1  \
+	VSLB  h1, h2, h2   \
+	VO    h0, h2, h0   \
+	VLEIB $7, $104, h1 \
+	VSLB  h1, h4, h3   \
+	VO    h3, h0, h0   \
+	VLEIB $7, $24, h1  \
+	VSRLB h1, h4, h1
+
+// if h > 2**130-5 then h -= 2**130-5
+#define MOD(h0, h1, t0, t1, t2) \
+	VZERO t0          \
+	VLEIG $1, $5, t0  \
+	VACCQ h0, t0, t1  \
+	VAQ   h0, t0, t0  \
+	VONE  t2          \
+	VLEIG $1, $-4, t2 \
+	VAQ   t2, t1, t1  \
+	VACCQ h1, t1, t1  \
+	VONE  t2          \
+	VAQ   t2, t1, t1  \
+	VN    h0, t1, t2  \
+	VNC   t0, t1, t1  \
+	VO    t1, t2, h0
+
+// func poly1305vx(out *[16]byte, m *byte, mlen uint64, key *[32]key)
+TEXT ·poly1305vx(SB), $0-32
+	// This code processes up to 2 blocks (32 bytes) per iteration
+	// using the algorithm described in:
+	// NEON crypto, Daniel J. Bernstein & Peter Schwabe
+	// https://cryptojedi.org/papers/neoncrypto-20120320.pdf
+	LMG out+0(FP), R1, R4 // R1=out, R2=m, R3=mlen, R4=key
+
+	// load MOD26, EX0, EX1 and EX2
+	MOVD $·constants<>(SB), R5
+	VLM  (R5), MOD26, EX2
+
+	// setup r
+	VL   (R4), T_0
+	MOVD $·keyMask<>(SB), R6
+	VL   (R6), T_1
+	VN   T_0, T_1, T_0
+	EXPAND(T_0, T_0, R_0, R_1, R_2, R_3, R_4)
+
+	// setup r*5
+	VLEIG $0, $5, T_0
+	VLEIG $1, $5, T_0
+
+	// store r (for final block)
+	VMLOF T_0, R_1, R5SAVE_1
+	VMLOF T_0, R_2, R5SAVE_2
+	VMLOF T_0, R_3, R5SAVE_3
+	VMLOF T_0, R_4, R5SAVE_4
+	VLGVG $0, R_0, RSAVE_0
+	VLGVG $0, R_1, RSAVE_1
+	VLGVG $0, R_2, RSAVE_2
+	VLGVG $0, R_3, RSAVE_3
+	VLGVG $0, R_4, RSAVE_4
+
+	// skip r**2 calculation
+	CMPBLE R3, $16, skip
+
+	// calculate r**2
+	MULTIPLY(R_0, R_1, R_2, R_3, R_4, R_0, R_1, R_2, R_3, R_4, R5SAVE_1, R5SAVE_2, R5SAVE_3, R5SAVE_4, H_0, H_1, H_2, H_3, H_4)
+	REDUCE(H_0, H_1, H_2, H_3, H_4)
+	VLEIG $0, $5, T_0
+	VLEIG $1, $5, T_0
+	VMLOF T_0, H_1, R5_1
+	VMLOF T_0, H_2, R5_2
+	VMLOF T_0, H_3, R5_3
+	VMLOF T_0, H_4, R5_4
+	VLR   H_0, R_0
+	VLR   H_1, R_1
+	VLR   H_2, R_2
+	VLR   H_3, R_3
+	VLR   H_4, R_4
+
+	// initialize h
+	VZERO H_0
+	VZERO H_1
+	VZERO H_2
+	VZERO H_3
+	VZERO H_4
+
+loop:
+	CMPBLE R3, $32, b2
+	VLM    (R2), T_0, T_1
+	SUB    $32, R3
+	MOVD   $32(R2), R2
+	EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4)
+	VLEIB  $4, $1, F_4
+	VLEIB  $12, $1, F_4
+
+multiply:
+	VAG    H_0, F_0, F_0
+	VAG    H_1, F_1, F_1
+	VAG    H_2, F_2, F_2
+	VAG    H_3, F_3, F_3
+	VAG    H_4, F_4, F_4
+	MULTIPLY(F_0, F_1, F_2, F_3, F_4, R_0, R_1, R_2, R_3, R_4, R5_1, R5_2, R5_3, R5_4, H_0, H_1, H_2, H_3, H_4)
+	REDUCE(H_0, H_1, H_2, H_3, H_4)
+	CMPBNE R3, $0, loop
+
+finish:
+	// sum vectors
+	VZERO  T_0
+	VSUMQG H_0, T_0, H_0
+	VSUMQG H_1, T_0, H_1
+	VSUMQG H_2, T_0, H_2
+	VSUMQG H_3, T_0, H_3
+	VSUMQG H_4, T_0, H_4
+
+	// h may be >= 2*(2**130-5) so we need to reduce it again
+	REDUCE(H_0, H_1, H_2, H_3, H_4)
+
+	// carry h1->h4
+	VESRLG $26, H_1, T_1
+	VN     MOD26, H_1, H_1
+	VAQ    T_1, H_2, H_2
+	VESRLG $26, H_2, T_2
+	VN     MOD26, H_2, H_2
+	VAQ    T_2, H_3, H_3
+	VESRLG $26, H_3, T_3
+	VN     MOD26, H_3, H_3
+	VAQ    T_3, H_4, H_4
+
+	// h is now < 2*(2**130-5)
+	// pack h into h1 (hi) and h0 (lo)
+	PACK(H_0, H_1, H_2, H_3, H_4)
+
+	// if h > 2**130-5 then h -= 2**130-5
+	MOD(H_0, H_1, T_0, T_1, T_2)
+
+	// h += s
+	MOVD  $·bswapMask<>(SB), R5
+	VL    (R5), T_1
+	VL    16(R4), T_0
+	VPERM T_0, T_0, T_1, T_0    // reverse bytes (to big)
+	VAQ   T_0, H_0, H_0
+	VPERM H_0, H_0, T_1, H_0    // reverse bytes (to little)
+	VST   H_0, (R1)
+
+	RET
+
+b2:
+	CMPBLE R3, $16, b1
+
+	// 2 blocks remaining
+	SUB    $17, R3
+	VL     (R2), T_0
+	VLL    R3, 16(R2), T_1
+	ADD    $1, R3
+	MOVBZ  $1, R0
+	CMPBEQ R3, $16, 2(PC)
+	VLVGB  R3, R0, T_1
+	EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4)
+	CMPBNE R3, $16, 2(PC)
+	VLEIB  $12, $1, F_4
+	VLEIB  $4, $1, F_4
+
+	// setup [r²,r]
+	VLVGG $1, RSAVE_0, R_0
+	VLVGG $1, RSAVE_1, R_1
+	VLVGG $1, RSAVE_2, R_2
+	VLVGG $1, RSAVE_3, R_3
+	VLVGG $1, RSAVE_4, R_4
+	VPDI  $0, R5_1, R5SAVE_1, R5_1
+	VPDI  $0, R5_2, R5SAVE_2, R5_2
+	VPDI  $0, R5_3, R5SAVE_3, R5_3
+	VPDI  $0, R5_4, R5SAVE_4, R5_4
+
+	MOVD $0, R3
+	BR   multiply
+
+skip:
+	VZERO H_0
+	VZERO H_1
+	VZERO H_2
+	VZERO H_3
+	VZERO H_4
+
+	CMPBEQ R3, $0, finish
+
+b1:
+	// 1 block remaining
+	SUB    $1, R3
+	VLL    R3, (R2), T_0
+	ADD    $1, R3
+	MOVBZ  $1, R0
+	CMPBEQ R3, $16, 2(PC)
+	VLVGB  R3, R0, T_0
+	VZERO  T_1
+	EXPAND(T_0, T_1, F_0, F_1, F_2, F_3, F_4)
+	CMPBNE R3, $16, 2(PC)
+	VLEIB  $4, $1, F_4
+	VLEIG  $1, $1, R_0
+	VZERO  R_1
+	VZERO  R_2
+	VZERO  R_3
+	VZERO  R_4
+	VZERO  R5_1
+	VZERO  R5_2
+	VZERO  R5_3
+	VZERO  R5_4
+
+	// setup [r, 1]
+	VLVGG $0, RSAVE_0, R_0
+	VLVGG $0, RSAVE_1, R_1
+	VLVGG $0, RSAVE_2, R_2
+	VLVGG $0, RSAVE_3, R_3
+	VLVGG $0, RSAVE_4, R_4
+	VPDI  $0, R5SAVE_1, R5_1, R5_1
+	VPDI  $0, R5SAVE_2, R5_2, R5_2
+	VPDI  $0, R5SAVE_3, R5_3, R5_3
+	VPDI  $0, R5SAVE_4, R5_4, R5_4
+
+	MOVD $0, R3
+	BR   multiply
+
+TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1
+	MOVD  $x-24(SP), R1
+	XC    $24, 0(R1), 0(R1) // clear the storage
+	MOVD  $2, R0            // R0 is the number of double words stored -1
+	WORD  $0xB2B01000       // STFLE 0(R1)
+	XOR   R0, R0            // reset the value of R0
+	MOVBZ z-8(SP), R1
+	AND   $0x40, R1
+	BEQ   novector
+
+vectorinstalled:
+	// check if the vector instruction has been enabled
+	VLEIB  $0, $0xF, V16
+	VLGVB  $0, V16, R1
+	CMPBNE R1, $0xF, novector
+	MOVB   $1, ret+0(FP)      // have vx
+	RET
+
+novector:
+	MOVB $0, ret+0(FP) // no vx
+	RET
diff --git a/src/ssl/test/runner/poly1305/sum_vmsl_s390x.s b/src/ssl/test/runner/poly1305/sum_vmsl_s390x.s
new file mode 100644
index 0000000..e548020
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/sum_vmsl_s390x.s
@@ -0,0 +1,931 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build s390x,go1.11,!gccgo,!appengine
+
+#include "textflag.h"
+
+// Implementation of Poly1305 using the vector facility (vx) and the VMSL instruction.
+
+// constants
+#define EX0   V1
+#define EX1   V2
+#define EX2   V3
+
+// temporaries
+#define T_0 V4
+#define T_1 V5
+#define T_2 V6
+#define T_3 V7
+#define T_4 V8
+#define T_5 V9
+#define T_6 V10
+#define T_7 V11
+#define T_8 V12
+#define T_9 V13
+#define T_10 V14
+
+// r**2 & r**4
+#define R_0  V15
+#define R_1  V16
+#define R_2  V17
+#define R5_1 V18
+#define R5_2 V19
+// key (r)
+#define RSAVE_0 R7
+#define RSAVE_1 R8
+#define RSAVE_2 R9
+#define R5SAVE_1 R10
+#define R5SAVE_2 R11
+
+// message block
+#define M0 V20
+#define M1 V21
+#define M2 V22
+#define M3 V23
+#define M4 V24
+#define M5 V25
+
+// accumulator
+#define H0_0 V26
+#define H1_0 V27
+#define H2_0 V28
+#define H0_1 V29
+#define H1_1 V30
+#define H2_1 V31
+
+GLOBL ·keyMask<>(SB), RODATA, $16
+DATA ·keyMask<>+0(SB)/8, $0xffffff0ffcffff0f
+DATA ·keyMask<>+8(SB)/8, $0xfcffff0ffcffff0f
+
+GLOBL ·bswapMask<>(SB), RODATA, $16
+DATA ·bswapMask<>+0(SB)/8, $0x0f0e0d0c0b0a0908
+DATA ·bswapMask<>+8(SB)/8, $0x0706050403020100
+
+GLOBL ·constants<>(SB), RODATA, $48
+// EX0
+DATA ·constants<>+0(SB)/8, $0x18191a1b1c1d1e1f
+DATA ·constants<>+8(SB)/8, $0x0000050403020100
+// EX1
+DATA ·constants<>+16(SB)/8, $0x18191a1b1c1d1e1f
+DATA ·constants<>+24(SB)/8, $0x00000a0908070605
+// EX2
+DATA ·constants<>+32(SB)/8, $0x18191a1b1c1d1e1f
+DATA ·constants<>+40(SB)/8, $0x0000000f0e0d0c0b
+
+GLOBL ·c<>(SB), RODATA, $48
+// EX0
+DATA ·c<>+0(SB)/8, $0x0000050403020100
+DATA ·c<>+8(SB)/8, $0x0000151413121110
+// EX1
+DATA ·c<>+16(SB)/8, $0x00000a0908070605
+DATA ·c<>+24(SB)/8, $0x00001a1918171615
+// EX2
+DATA ·c<>+32(SB)/8, $0x0000000f0e0d0c0b
+DATA ·c<>+40(SB)/8, $0x0000001f1e1d1c1b
+
+GLOBL ·reduce<>(SB), RODATA, $32
+// 44 bit
+DATA ·reduce<>+0(SB)/8, $0x0
+DATA ·reduce<>+8(SB)/8, $0xfffffffffff
+// 42 bit
+DATA ·reduce<>+16(SB)/8, $0x0
+DATA ·reduce<>+24(SB)/8, $0x3ffffffffff
+
+// h = (f*g) % (2**130-5) [partial reduction]
+// uses T_0...T_9 temporary registers
+// input: m02_0, m02_1, m02_2, m13_0, m13_1, m13_2, r_0, r_1, r_2, r5_1, r5_2, m4_0, m4_1, m4_2, m5_0, m5_1, m5_2
+// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8, t9
+// output: m02_0, m02_1, m02_2, m13_0, m13_1, m13_2
+#define MULTIPLY(m02_0, m02_1, m02_2, m13_0, m13_1, m13_2, r_0, r_1, r_2, r5_1, r5_2, m4_0, m4_1, m4_2, m5_0, m5_1, m5_2, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9) \
+	\ // Eliminate the dependency for the last 2 VMSLs
+	VMSLG m02_0, r_2, m4_2, m4_2                       \
+	VMSLG m13_0, r_2, m5_2, m5_2                       \ // 8 VMSLs pipelined
+	VMSLG m02_0, r_0, m4_0, m4_0                       \
+	VMSLG m02_1, r5_2, V0, T_0                         \
+	VMSLG m02_0, r_1, m4_1, m4_1                       \
+	VMSLG m02_1, r_0, V0, T_1                          \
+	VMSLG m02_1, r_1, V0, T_2                          \
+	VMSLG m02_2, r5_1, V0, T_3                         \
+	VMSLG m02_2, r5_2, V0, T_4                         \
+	VMSLG m13_0, r_0, m5_0, m5_0                       \
+	VMSLG m13_1, r5_2, V0, T_5                         \
+	VMSLG m13_0, r_1, m5_1, m5_1                       \
+	VMSLG m13_1, r_0, V0, T_6                          \
+	VMSLG m13_1, r_1, V0, T_7                          \
+	VMSLG m13_2, r5_1, V0, T_8                         \
+	VMSLG m13_2, r5_2, V0, T_9                         \
+	VMSLG m02_2, r_0, m4_2, m4_2                       \
+	VMSLG m13_2, r_0, m5_2, m5_2                       \
+	VAQ   m4_0, T_0, m02_0                             \
+	VAQ   m4_1, T_1, m02_1                             \
+	VAQ   m5_0, T_5, m13_0                             \
+	VAQ   m5_1, T_6, m13_1                             \
+	VAQ   m02_0, T_3, m02_0                            \
+	VAQ   m02_1, T_4, m02_1                            \
+	VAQ   m13_0, T_8, m13_0                            \
+	VAQ   m13_1, T_9, m13_1                            \
+	VAQ   m4_2, T_2, m02_2                             \
+	VAQ   m5_2, T_7, m13_2                             \
+
+// SQUARE uses three limbs of r and r_2*5 to output square of r
+// uses T_1, T_5 and T_7 temporary registers
+// input: r_0, r_1, r_2, r5_2
+// temp: TEMP0, TEMP1, TEMP2
+// output: p0, p1, p2
+#define SQUARE(r_0, r_1, r_2, r5_2, p0, p1, p2, TEMP0, TEMP1, TEMP2) \
+	VMSLG r_0, r_0, p0, p0     \
+	VMSLG r_1, r5_2, V0, TEMP0 \
+	VMSLG r_2, r5_2, p1, p1    \
+	VMSLG r_0, r_1, V0, TEMP1  \
+	VMSLG r_1, r_1, p2, p2     \
+	VMSLG r_0, r_2, V0, TEMP2  \
+	VAQ   TEMP0, p0, p0        \
+	VAQ   TEMP1, p1, p1        \
+	VAQ   TEMP2, p2, p2        \
+	VAQ   TEMP0, p0, p0        \
+	VAQ   TEMP1, p1, p1        \
+	VAQ   TEMP2, p2, p2        \
+
+// carry h0->h1->h2->h0 || h3->h4->h5->h3
+// uses T_2, T_4, T_5, T_7, T_8, T_9
+//       t6,  t7,  t8,  t9, t10, t11
+// input: h0, h1, h2, h3, h4, h5
+// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11
+// output: h0, h1, h2, h3, h4, h5
+#define REDUCE(h0, h1, h2, h3, h4, h5, t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) \
+	VLM    (R12), t6, t7  \ // 44 and 42 bit clear mask
+	VLEIB  $7, $0x28, t10 \ // 5 byte shift mask
+	VREPIB $4, t8         \ // 4 bit shift mask
+	VREPIB $2, t11        \ // 2 bit shift mask
+	VSRLB  t10, h0, t0    \ // h0 byte shift
+	VSRLB  t10, h1, t1    \ // h1 byte shift
+	VSRLB  t10, h2, t2    \ // h2 byte shift
+	VSRLB  t10, h3, t3    \ // h3 byte shift
+	VSRLB  t10, h4, t4    \ // h4 byte shift
+	VSRLB  t10, h5, t5    \ // h5 byte shift
+	VSRL   t8, t0, t0     \ // h0 bit shift
+	VSRL   t8, t1, t1     \ // h2 bit shift
+	VSRL   t11, t2, t2    \ // h2 bit shift
+	VSRL   t8, t3, t3     \ // h3 bit shift
+	VSRL   t8, t4, t4     \ // h4 bit shift
+	VESLG  $2, t2, t9     \ // h2 carry x5
+	VSRL   t11, t5, t5    \ // h5 bit shift
+	VN     t6, h0, h0     \ // h0 clear carry
+	VAQ    t2, t9, t2     \ // h2 carry x5
+	VESLG  $2, t5, t9     \ // h5 carry x5
+	VN     t6, h1, h1     \ // h1 clear carry
+	VN     t7, h2, h2     \ // h2 clear carry
+	VAQ    t5, t9, t5     \ // h5 carry x5
+	VN     t6, h3, h3     \ // h3 clear carry
+	VN     t6, h4, h4     \ // h4 clear carry
+	VN     t7, h5, h5     \ // h5 clear carry
+	VAQ    t0, h1, h1     \ // h0->h1
+	VAQ    t3, h4, h4     \ // h3->h4
+	VAQ    t1, h2, h2     \ // h1->h2
+	VAQ    t4, h5, h5     \ // h4->h5
+	VAQ    t2, h0, h0     \ // h2->h0
+	VAQ    t5, h3, h3     \ // h5->h3
+	VREPG  $1, t6, t6     \ // 44 and 42 bit masks across both halves
+	VREPG  $1, t7, t7     \
+	VSLDB  $8, h0, h0, h0 \ // set up [h0/1/2, h3/4/5]
+	VSLDB  $8, h1, h1, h1 \
+	VSLDB  $8, h2, h2, h2 \
+	VO     h0, h3, h3     \
+	VO     h1, h4, h4     \
+	VO     h2, h5, h5     \
+	VESRLG $44, h3, t0    \ // 44 bit shift right
+	VESRLG $44, h4, t1    \
+	VESRLG $42, h5, t2    \
+	VN     t6, h3, h3     \ // clear carry bits
+	VN     t6, h4, h4     \
+	VN     t7, h5, h5     \
+	VESLG  $2, t2, t9     \ // multiply carry by 5
+	VAQ    t9, t2, t2     \
+	VAQ    t0, h4, h4     \
+	VAQ    t1, h5, h5     \
+	VAQ    t2, h3, h3     \
+
+// carry h0->h1->h2->h0
+// input: h0, h1, h2
+// temp: t0, t1, t2, t3, t4, t5, t6, t7, t8
+// output: h0, h1, h2
+#define REDUCE2(h0, h1, h2, t0, t1, t2, t3, t4, t5, t6, t7, t8) \
+	VLEIB  $7, $0x28, t3 \ // 5 byte shift mask
+	VREPIB $4, t4        \ // 4 bit shift mask
+	VREPIB $2, t7        \ // 2 bit shift mask
+	VGBM   $0x003F, t5   \ // mask to clear carry bits
+	VSRLB  t3, h0, t0    \
+	VSRLB  t3, h1, t1    \
+	VSRLB  t3, h2, t2    \
+	VESRLG $4, t5, t5    \ // 44 bit clear mask
+	VSRL   t4, t0, t0    \
+	VSRL   t4, t1, t1    \
+	VSRL   t7, t2, t2    \
+	VESRLG $2, t5, t6    \ // 42 bit clear mask
+	VESLG  $2, t2, t8    \
+	VAQ    t8, t2, t2    \
+	VN     t5, h0, h0    \
+	VN     t5, h1, h1    \
+	VN     t6, h2, h2    \
+	VAQ    t0, h1, h1    \
+	VAQ    t1, h2, h2    \
+	VAQ    t2, h0, h0    \
+	VSRLB  t3, h0, t0    \
+	VSRLB  t3, h1, t1    \
+	VSRLB  t3, h2, t2    \
+	VSRL   t4, t0, t0    \
+	VSRL   t4, t1, t1    \
+	VSRL   t7, t2, t2    \
+	VN     t5, h0, h0    \
+	VN     t5, h1, h1    \
+	VESLG  $2, t2, t8    \
+	VN     t6, h2, h2    \
+	VAQ    t0, h1, h1    \
+	VAQ    t8, t2, t2    \
+	VAQ    t1, h2, h2    \
+	VAQ    t2, h0, h0    \
+
+// expands two message blocks into the lower halfs of the d registers
+// moves the contents of the d registers into upper halfs
+// input: in1, in2, d0, d1, d2, d3, d4, d5
+// temp: TEMP0, TEMP1, TEMP2, TEMP3
+// output: d0, d1, d2, d3, d4, d5
+#define EXPACC(in1, in2, d0, d1, d2, d3, d4, d5, TEMP0, TEMP1, TEMP2, TEMP3) \
+	VGBM   $0xff3f, TEMP0      \
+	VGBM   $0xff1f, TEMP1      \
+	VESLG  $4, d1, TEMP2       \
+	VESLG  $4, d4, TEMP3       \
+	VESRLG $4, TEMP0, TEMP0    \
+	VPERM  in1, d0, EX0, d0    \
+	VPERM  in2, d3, EX0, d3    \
+	VPERM  in1, d2, EX2, d2    \
+	VPERM  in2, d5, EX2, d5    \
+	VPERM  in1, TEMP2, EX1, d1 \
+	VPERM  in2, TEMP3, EX1, d4 \
+	VN     TEMP0, d0, d0       \
+	VN     TEMP0, d3, d3       \
+	VESRLG $4, d1, d1          \
+	VESRLG $4, d4, d4          \
+	VN     TEMP1, d2, d2       \
+	VN     TEMP1, d5, d5       \
+	VN     TEMP0, d1, d1       \
+	VN     TEMP0, d4, d4       \
+
+// expands one message block into the lower halfs of the d registers
+// moves the contents of the d registers into upper halfs
+// input: in, d0, d1, d2
+// temp: TEMP0, TEMP1, TEMP2
+// output: d0, d1, d2
+#define EXPACC2(in, d0, d1, d2, TEMP0, TEMP1, TEMP2) \
+	VGBM   $0xff3f, TEMP0     \
+	VESLG  $4, d1, TEMP2      \
+	VGBM   $0xff1f, TEMP1     \
+	VPERM  in, d0, EX0, d0    \
+	VESRLG $4, TEMP0, TEMP0   \
+	VPERM  in, d2, EX2, d2    \
+	VPERM  in, TEMP2, EX1, d1 \
+	VN     TEMP0, d0, d0      \
+	VN     TEMP1, d2, d2      \
+	VESRLG $4, d1, d1         \
+	VN     TEMP0, d1, d1      \
+
+// pack h2:h0 into h1:h0 (no carry)
+// input: h0, h1, h2
+// output: h0, h1, h2
+#define PACK(h0, h1, h2) \
+	VMRLG  h1, h2, h2  \ // copy h1 to upper half h2
+	VESLG  $44, h1, h1 \ // shift limb 1 44 bits, leaving 20
+	VO     h0, h1, h0  \ // combine h0 with 20 bits from limb 1
+	VESRLG $20, h2, h1 \ // put top 24 bits of limb 1 into h1
+	VLEIG  $1, $0, h1  \ // clear h2 stuff from lower half of h1
+	VO     h0, h1, h0  \ // h0 now has 88 bits (limb 0 and 1)
+	VLEIG  $0, $0, h2  \ // clear upper half of h2
+	VESRLG $40, h2, h1 \ // h1 now has upper two bits of result
+	VLEIB  $7, $88, h1 \ // for byte shift (11 bytes)
+	VSLB   h1, h2, h2  \ // shift h2 11 bytes to the left
+	VO     h0, h2, h0  \ // combine h0 with 20 bits from limb 1
+	VLEIG  $0, $0, h1  \ // clear upper half of h1
+
+// if h > 2**130-5 then h -= 2**130-5
+// input: h0, h1
+// temp: t0, t1, t2
+// output: h0
+#define MOD(h0, h1, t0, t1, t2) \
+	VZERO t0          \
+	VLEIG $1, $5, t0  \
+	VACCQ h0, t0, t1  \
+	VAQ   h0, t0, t0  \
+	VONE  t2          \
+	VLEIG $1, $-4, t2 \
+	VAQ   t2, t1, t1  \
+	VACCQ h1, t1, t1  \
+	VONE  t2          \
+	VAQ   t2, t1, t1  \
+	VN    h0, t1, t2  \
+	VNC   t0, t1, t1  \
+	VO    t1, t2, h0  \
+
+// func poly1305vmsl(out *[16]byte, m *byte, mlen uint64, key *[32]key)
+TEXT ·poly1305vmsl(SB), $0-32
+	// This code processes 6 + up to 4 blocks (32 bytes) per iteration
+	// using the algorithm described in:
+	// NEON crypto, Daniel J. Bernstein & Peter Schwabe
+	// https://cryptojedi.org/papers/neoncrypto-20120320.pdf
+	// And as moddified for VMSL as described in
+	// Accelerating Poly1305 Cryptographic Message Authentication on the z14
+	// O'Farrell et al, CASCON 2017, p48-55
+	// https://ibm.ent.box.com/s/jf9gedj0e9d2vjctfyh186shaztavnht
+
+	LMG   out+0(FP), R1, R4 // R1=out, R2=m, R3=mlen, R4=key
+	VZERO V0                // c
+
+	// load EX0, EX1 and EX2
+	MOVD $·constants<>(SB), R5
+	VLM  (R5), EX0, EX2        // c
+
+	// setup r
+	VL    (R4), T_0
+	MOVD  $·keyMask<>(SB), R6
+	VL    (R6), T_1
+	VN    T_0, T_1, T_0
+	VZERO T_2                 // limbs for r
+	VZERO T_3
+	VZERO T_4
+	EXPACC2(T_0, T_2, T_3, T_4, T_1, T_5, T_7)
+
+	// T_2, T_3, T_4: [0, r]
+
+	// setup r*20
+	VLEIG $0, $0, T_0
+	VLEIG $1, $20, T_0       // T_0: [0, 20]
+	VZERO T_5
+	VZERO T_6
+	VMSLG T_0, T_3, T_5, T_5
+	VMSLG T_0, T_4, T_6, T_6
+
+	// store r for final block in GR
+	VLGVG $1, T_2, RSAVE_0  // c
+	VLGVG $1, T_3, RSAVE_1  // c
+	VLGVG $1, T_4, RSAVE_2  // c
+	VLGVG $1, T_5, R5SAVE_1 // c
+	VLGVG $1, T_6, R5SAVE_2 // c
+
+	// initialize h
+	VZERO H0_0
+	VZERO H1_0
+	VZERO H2_0
+	VZERO H0_1
+	VZERO H1_1
+	VZERO H2_1
+
+	// initialize pointer for reduce constants
+	MOVD $·reduce<>(SB), R12
+
+	// calculate r**2 and 20*(r**2)
+	VZERO R_0
+	VZERO R_1
+	VZERO R_2
+	SQUARE(T_2, T_3, T_4, T_6, R_0, R_1, R_2, T_1, T_5, T_7)
+	REDUCE2(R_0, R_1, R_2, M0, M1, M2, M3, M4, R5_1, R5_2, M5, T_1)
+	VZERO R5_1
+	VZERO R5_2
+	VMSLG T_0, R_1, R5_1, R5_1
+	VMSLG T_0, R_2, R5_2, R5_2
+
+	// skip r**4 calculation if 3 blocks or less
+	CMPBLE R3, $48, b4
+
+	// calculate r**4 and 20*(r**4)
+	VZERO T_8
+	VZERO T_9
+	VZERO T_10
+	SQUARE(R_0, R_1, R_2, R5_2, T_8, T_9, T_10, T_1, T_5, T_7)
+	REDUCE2(T_8, T_9, T_10, M0, M1, M2, M3, M4, T_2, T_3, M5, T_1)
+	VZERO T_2
+	VZERO T_3
+	VMSLG T_0, T_9, T_2, T_2
+	VMSLG T_0, T_10, T_3, T_3
+
+	// put r**2 to the right and r**4 to the left of R_0, R_1, R_2
+	VSLDB $8, T_8, T_8, T_8
+	VSLDB $8, T_9, T_9, T_9
+	VSLDB $8, T_10, T_10, T_10
+	VSLDB $8, T_2, T_2, T_2
+	VSLDB $8, T_3, T_3, T_3
+
+	VO T_8, R_0, R_0
+	VO T_9, R_1, R_1
+	VO T_10, R_2, R_2
+	VO T_2, R5_1, R5_1
+	VO T_3, R5_2, R5_2
+
+	CMPBLE R3, $80, load // less than or equal to 5 blocks in message
+
+	// 6(or 5+1) blocks
+	SUB    $81, R3
+	VLM    (R2), M0, M4
+	VLL    R3, 80(R2), M5
+	ADD    $1, R3
+	MOVBZ  $1, R0
+	CMPBGE R3, $16, 2(PC)
+	VLVGB  R3, R0, M5
+	MOVD   $96(R2), R2
+	EXPACC(M0, M1, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3)
+	EXPACC(M2, M3, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3)
+	VLEIB  $2, $1, H2_0
+	VLEIB  $2, $1, H2_1
+	VLEIB  $10, $1, H2_0
+	VLEIB  $10, $1, H2_1
+
+	VZERO  M0
+	VZERO  M1
+	VZERO  M2
+	VZERO  M3
+	VZERO  T_4
+	VZERO  T_10
+	EXPACC(M4, M5, M0, M1, M2, M3, T_4, T_10, T_0, T_1, T_2, T_3)
+	VLR    T_4, M4
+	VLEIB  $10, $1, M2
+	CMPBLT R3, $16, 2(PC)
+	VLEIB  $10, $1, T_10
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M2, M3, M4, T_4, T_5, T_2, T_7, T_8, T_9)
+	VMRHG  V0, H0_1, H0_0
+	VMRHG  V0, H1_1, H1_0
+	VMRHG  V0, H2_1, H2_0
+	VMRLG  V0, H0_1, H0_1
+	VMRLG  V0, H1_1, H1_1
+	VMRLG  V0, H2_1, H2_1
+
+	SUB    $16, R3
+	CMPBLE R3, $0, square
+
+load:
+	// load EX0, EX1 and EX2
+	MOVD $·c<>(SB), R5
+	VLM  (R5), EX0, EX2
+
+loop:
+	CMPBLE R3, $64, add // b4	// last 4 or less blocks left
+
+	// next 4 full blocks
+	VLM  (R2), M2, M5
+	SUB  $64, R3
+	MOVD $64(R2), R2
+	REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, T_0, T_1, T_3, T_4, T_5, T_2, T_7, T_8, T_9)
+
+	// expacc in-lined to create [m2, m3] limbs
+	VGBM   $0x3f3f, T_0     // 44 bit clear mask
+	VGBM   $0x1f1f, T_1     // 40 bit clear mask
+	VPERM  M2, M3, EX0, T_3
+	VESRLG $4, T_0, T_0     // 44 bit clear mask ready
+	VPERM  M2, M3, EX1, T_4
+	VPERM  M2, M3, EX2, T_5
+	VN     T_0, T_3, T_3
+	VESRLG $4, T_4, T_4
+	VN     T_1, T_5, T_5
+	VN     T_0, T_4, T_4
+	VMRHG  H0_1, T_3, H0_0
+	VMRHG  H1_1, T_4, H1_0
+	VMRHG  H2_1, T_5, H2_0
+	VMRLG  H0_1, T_3, H0_1
+	VMRLG  H1_1, T_4, H1_1
+	VMRLG  H2_1, T_5, H2_1
+	VLEIB  $10, $1, H2_0
+	VLEIB  $10, $1, H2_1
+	VPERM  M4, M5, EX0, T_3
+	VPERM  M4, M5, EX1, T_4
+	VPERM  M4, M5, EX2, T_5
+	VN     T_0, T_3, T_3
+	VESRLG $4, T_4, T_4
+	VN     T_1, T_5, T_5
+	VN     T_0, T_4, T_4
+	VMRHG  V0, T_3, M0
+	VMRHG  V0, T_4, M1
+	VMRHG  V0, T_5, M2
+	VMRLG  V0, T_3, M3
+	VMRLG  V0, T_4, M4
+	VMRLG  V0, T_5, M5
+	VLEIB  $10, $1, M2
+	VLEIB  $10, $1, M5
+
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	CMPBNE R3, $0, loop
+	REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9)
+	VMRHG  V0, H0_1, H0_0
+	VMRHG  V0, H1_1, H1_0
+	VMRHG  V0, H2_1, H2_0
+	VMRLG  V0, H0_1, H0_1
+	VMRLG  V0, H1_1, H1_1
+	VMRLG  V0, H2_1, H2_1
+
+	// load EX0, EX1, EX2
+	MOVD $·constants<>(SB), R5
+	VLM  (R5), EX0, EX2
+
+	// sum vectors
+	VAQ H0_0, H0_1, H0_0
+	VAQ H1_0, H1_1, H1_0
+	VAQ H2_0, H2_1, H2_0
+
+	// h may be >= 2*(2**130-5) so we need to reduce it again
+	// M0...M4 are used as temps here
+	REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5)
+
+next:  // carry h1->h2
+	VLEIB  $7, $0x28, T_1
+	VREPIB $4, T_2
+	VGBM   $0x003F, T_3
+	VESRLG $4, T_3
+
+	// byte shift
+	VSRLB T_1, H1_0, T_4
+
+	// bit shift
+	VSRL T_2, T_4, T_4
+
+	// clear h1 carry bits
+	VN T_3, H1_0, H1_0
+
+	// add carry
+	VAQ T_4, H2_0, H2_0
+
+	// h is now < 2*(2**130-5)
+	// pack h into h1 (hi) and h0 (lo)
+	PACK(H0_0, H1_0, H2_0)
+
+	// if h > 2**130-5 then h -= 2**130-5
+	MOD(H0_0, H1_0, T_0, T_1, T_2)
+
+	// h += s
+	MOVD  $·bswapMask<>(SB), R5
+	VL    (R5), T_1
+	VL    16(R4), T_0
+	VPERM T_0, T_0, T_1, T_0    // reverse bytes (to big)
+	VAQ   T_0, H0_0, H0_0
+	VPERM H0_0, H0_0, T_1, H0_0 // reverse bytes (to little)
+	VST   H0_0, (R1)
+	RET
+
+add:
+	// load EX0, EX1, EX2
+	MOVD $·constants<>(SB), R5
+	VLM  (R5), EX0, EX2
+
+	REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9)
+	VMRHG  V0, H0_1, H0_0
+	VMRHG  V0, H1_1, H1_0
+	VMRHG  V0, H2_1, H2_0
+	VMRLG  V0, H0_1, H0_1
+	VMRLG  V0, H1_1, H1_1
+	VMRLG  V0, H2_1, H2_1
+	CMPBLE R3, $64, b4
+
+b4:
+	CMPBLE R3, $48, b3 // 3 blocks or less
+
+	// 4(3+1) blocks remaining
+	SUB    $49, R3
+	VLM    (R2), M0, M2
+	VLL    R3, 48(R2), M3
+	ADD    $1, R3
+	MOVBZ  $1, R0
+	CMPBEQ R3, $16, 2(PC)
+	VLVGB  R3, R0, M3
+	MOVD   $64(R2), R2
+	EXPACC(M0, M1, H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_0, T_1, T_2, T_3)
+	VLEIB  $10, $1, H2_0
+	VLEIB  $10, $1, H2_1
+	VZERO  M0
+	VZERO  M1
+	VZERO  M4
+	VZERO  M5
+	VZERO  T_4
+	VZERO  T_10
+	EXPACC(M2, M3, M0, M1, M4, M5, T_4, T_10, T_0, T_1, T_2, T_3)
+	VLR    T_4, M2
+	VLEIB  $10, $1, M4
+	CMPBNE R3, $16, 2(PC)
+	VLEIB  $10, $1, T_10
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M4, M5, M2, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M3, M4, M5, T_4, T_5, T_2, T_7, T_8, T_9)
+	VMRHG  V0, H0_1, H0_0
+	VMRHG  V0, H1_1, H1_0
+	VMRHG  V0, H2_1, H2_0
+	VMRLG  V0, H0_1, H0_1
+	VMRLG  V0, H1_1, H1_1
+	VMRLG  V0, H2_1, H2_1
+	SUB    $16, R3
+	CMPBLE R3, $0, square // this condition must always hold true!
+
+b3:
+	CMPBLE R3, $32, b2
+
+	// 3 blocks remaining
+
+	// setup [r²,r]
+	VSLDB $8, R_0, R_0, R_0
+	VSLDB $8, R_1, R_1, R_1
+	VSLDB $8, R_2, R_2, R_2
+	VSLDB $8, R5_1, R5_1, R5_1
+	VSLDB $8, R5_2, R5_2, R5_2
+
+	VLVGG $1, RSAVE_0, R_0
+	VLVGG $1, RSAVE_1, R_1
+	VLVGG $1, RSAVE_2, R_2
+	VLVGG $1, R5SAVE_1, R5_1
+	VLVGG $1, R5SAVE_2, R5_2
+
+	// setup [h0, h1]
+	VSLDB $8, H0_0, H0_0, H0_0
+	VSLDB $8, H1_0, H1_0, H1_0
+	VSLDB $8, H2_0, H2_0, H2_0
+	VO    H0_1, H0_0, H0_0
+	VO    H1_1, H1_0, H1_0
+	VO    H2_1, H2_0, H2_0
+	VZERO H0_1
+	VZERO H1_1
+	VZERO H2_1
+
+	VZERO M0
+	VZERO M1
+	VZERO M2
+	VZERO M3
+	VZERO M4
+	VZERO M5
+
+	// H*[r**2, r]
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, H0_1, H1_1, T_10, M5)
+
+	SUB    $33, R3
+	VLM    (R2), M0, M1
+	VLL    R3, 32(R2), M2
+	ADD    $1, R3
+	MOVBZ  $1, R0
+	CMPBEQ R3, $16, 2(PC)
+	VLVGB  R3, R0, M2
+
+	// H += m0
+	VZERO T_1
+	VZERO T_2
+	VZERO T_3
+	EXPACC2(M0, T_1, T_2, T_3, T_4, T_5, T_6)
+	VLEIB $10, $1, T_3
+	VAG   H0_0, T_1, H0_0
+	VAG   H1_0, T_2, H1_0
+	VAG   H2_0, T_3, H2_0
+
+	VZERO M0
+	VZERO M3
+	VZERO M4
+	VZERO M5
+	VZERO T_10
+
+	// (H+m0)*r
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M3, M4, M5, V0, T_10, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE2(H0_0, H1_0, H2_0, M0, M3, M4, M5, T_10, H0_1, H1_1, H2_1, T_9)
+
+	// H += m1
+	VZERO V0
+	VZERO T_1
+	VZERO T_2
+	VZERO T_3
+	EXPACC2(M1, T_1, T_2, T_3, T_4, T_5, T_6)
+	VLEIB $10, $1, T_3
+	VAQ   H0_0, T_1, H0_0
+	VAQ   H1_0, T_2, H1_0
+	VAQ   H2_0, T_3, H2_0
+	REDUCE2(H0_0, H1_0, H2_0, M0, M3, M4, M5, T_9, H0_1, H1_1, H2_1, T_10)
+
+	// [H, m2] * [r**2, r]
+	EXPACC2(M2, H0_0, H1_0, H2_0, T_1, T_2, T_3)
+	CMPBNE R3, $16, 2(PC)
+	VLEIB  $10, $1, H2_0
+	VZERO  M0
+	VZERO  M1
+	VZERO  M2
+	VZERO  M3
+	VZERO  M4
+	VZERO  M5
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, H0_1, H1_1, M5, T_10)
+	SUB    $16, R3
+	CMPBLE R3, $0, next   // this condition must always hold true!
+
+b2:
+	CMPBLE R3, $16, b1
+
+	// 2 blocks remaining
+
+	// setup [r²,r]
+	VSLDB $8, R_0, R_0, R_0
+	VSLDB $8, R_1, R_1, R_1
+	VSLDB $8, R_2, R_2, R_2
+	VSLDB $8, R5_1, R5_1, R5_1
+	VSLDB $8, R5_2, R5_2, R5_2
+
+	VLVGG $1, RSAVE_0, R_0
+	VLVGG $1, RSAVE_1, R_1
+	VLVGG $1, RSAVE_2, R_2
+	VLVGG $1, R5SAVE_1, R5_1
+	VLVGG $1, R5SAVE_2, R5_2
+
+	// setup [h0, h1]
+	VSLDB $8, H0_0, H0_0, H0_0
+	VSLDB $8, H1_0, H1_0, H1_0
+	VSLDB $8, H2_0, H2_0, H2_0
+	VO    H0_1, H0_0, H0_0
+	VO    H1_1, H1_0, H1_0
+	VO    H2_1, H2_0, H2_0
+	VZERO H0_1
+	VZERO H1_1
+	VZERO H2_1
+
+	VZERO M0
+	VZERO M1
+	VZERO M2
+	VZERO M3
+	VZERO M4
+	VZERO M5
+
+	// H*[r**2, r]
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, T_10, M0, M1, M2, M3, M4, T_4, T_5, T_2, T_7, T_8, T_9)
+	VMRHG V0, H0_1, H0_0
+	VMRHG V0, H1_1, H1_0
+	VMRHG V0, H2_1, H2_0
+	VMRLG V0, H0_1, H0_1
+	VMRLG V0, H1_1, H1_1
+	VMRLG V0, H2_1, H2_1
+
+	// move h to the left and 0s at the right
+	VSLDB $8, H0_0, H0_0, H0_0
+	VSLDB $8, H1_0, H1_0, H1_0
+	VSLDB $8, H2_0, H2_0, H2_0
+
+	// get message blocks and append 1 to start
+	SUB    $17, R3
+	VL     (R2), M0
+	VLL    R3, 16(R2), M1
+	ADD    $1, R3
+	MOVBZ  $1, R0
+	CMPBEQ R3, $16, 2(PC)
+	VLVGB  R3, R0, M1
+	VZERO  T_6
+	VZERO  T_7
+	VZERO  T_8
+	EXPACC2(M0, T_6, T_7, T_8, T_1, T_2, T_3)
+	EXPACC2(M1, T_6, T_7, T_8, T_1, T_2, T_3)
+	VLEIB  $2, $1, T_8
+	CMPBNE R3, $16, 2(PC)
+	VLEIB  $10, $1, T_8
+
+	// add [m0, m1] to h
+	VAG H0_0, T_6, H0_0
+	VAG H1_0, T_7, H1_0
+	VAG H2_0, T_8, H2_0
+
+	VZERO M2
+	VZERO M3
+	VZERO M4
+	VZERO M5
+	VZERO T_10
+	VZERO M0
+
+	// at this point R_0 .. R5_2 look like [r**2, r]
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M2, M3, M4, M5, T_10, M0, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE2(H0_0, H1_0, H2_0, M2, M3, M4, M5, T_9, H0_1, H1_1, H2_1, T_10)
+	SUB    $16, R3, R3
+	CMPBLE R3, $0, next
+
+b1:
+	CMPBLE R3, $0, next
+
+	// 1 block remaining
+
+	// setup [r²,r]
+	VSLDB $8, R_0, R_0, R_0
+	VSLDB $8, R_1, R_1, R_1
+	VSLDB $8, R_2, R_2, R_2
+	VSLDB $8, R5_1, R5_1, R5_1
+	VSLDB $8, R5_2, R5_2, R5_2
+
+	VLVGG $1, RSAVE_0, R_0
+	VLVGG $1, RSAVE_1, R_1
+	VLVGG $1, RSAVE_2, R_2
+	VLVGG $1, R5SAVE_1, R5_1
+	VLVGG $1, R5SAVE_2, R5_2
+
+	// setup [h0, h1]
+	VSLDB $8, H0_0, H0_0, H0_0
+	VSLDB $8, H1_0, H1_0, H1_0
+	VSLDB $8, H2_0, H2_0, H2_0
+	VO    H0_1, H0_0, H0_0
+	VO    H1_1, H1_0, H1_0
+	VO    H2_1, H2_0, H2_0
+	VZERO H0_1
+	VZERO H1_1
+	VZERO H2_1
+
+	VZERO M0
+	VZERO M1
+	VZERO M2
+	VZERO M3
+	VZERO M4
+	VZERO M5
+
+	// H*[r**2, r]
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5)
+
+	// set up [0, m0] limbs
+	SUB    $1, R3
+	VLL    R3, (R2), M0
+	ADD    $1, R3
+	MOVBZ  $1, R0
+	CMPBEQ R3, $16, 2(PC)
+	VLVGB  R3, R0, M0
+	VZERO  T_1
+	VZERO  T_2
+	VZERO  T_3
+	EXPACC2(M0, T_1, T_2, T_3, T_4, T_5, T_6)// limbs: [0, m]
+	CMPBNE R3, $16, 2(PC)
+	VLEIB  $10, $1, T_3
+
+	// h+m0
+	VAQ H0_0, T_1, H0_0
+	VAQ H1_0, T_2, H1_0
+	VAQ H2_0, T_3, H2_0
+
+	VZERO M0
+	VZERO M1
+	VZERO M2
+	VZERO M3
+	VZERO M4
+	VZERO M5
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5)
+
+	BR next
+
+square:
+	// setup [r²,r]
+	VSLDB $8, R_0, R_0, R_0
+	VSLDB $8, R_1, R_1, R_1
+	VSLDB $8, R_2, R_2, R_2
+	VSLDB $8, R5_1, R5_1, R5_1
+	VSLDB $8, R5_2, R5_2, R5_2
+
+	VLVGG $1, RSAVE_0, R_0
+	VLVGG $1, RSAVE_1, R_1
+	VLVGG $1, RSAVE_2, R_2
+	VLVGG $1, R5SAVE_1, R5_1
+	VLVGG $1, R5SAVE_2, R5_2
+
+	// setup [h0, h1]
+	VSLDB $8, H0_0, H0_0, H0_0
+	VSLDB $8, H1_0, H1_0, H1_0
+	VSLDB $8, H2_0, H2_0, H2_0
+	VO    H0_1, H0_0, H0_0
+	VO    H1_1, H1_0, H1_0
+	VO    H2_1, H2_0, H2_0
+	VZERO H0_1
+	VZERO H1_1
+	VZERO H2_1
+
+	VZERO M0
+	VZERO M1
+	VZERO M2
+	VZERO M3
+	VZERO M4
+	VZERO M5
+
+	// (h0*r**2) + (h1*r)
+	MULTIPLY(H0_0, H1_0, H2_0, H0_1, H1_1, H2_1, R_0, R_1, R_2, R5_1, R5_2, M0, M1, M2, M3, M4, M5, T_0, T_1, T_2, T_3, T_4, T_5, T_6, T_7, T_8, T_9)
+	REDUCE2(H0_0, H1_0, H2_0, M0, M1, M2, M3, M4, T_9, T_10, H0_1, M5)
+	BR next
+
+TEXT ·hasVMSLFacility(SB), NOSPLIT, $24-1
+	MOVD  $x-24(SP), R1
+	XC    $24, 0(R1), 0(R1) // clear the storage
+	MOVD  $2, R0            // R0 is the number of double words stored -1
+	WORD  $0xB2B01000       // STFLE 0(R1)
+	XOR   R0, R0            // reset the value of R0
+	MOVBZ z-8(SP), R1
+	AND   $0x01, R1
+	BEQ   novmsl
+
+vectorinstalled:
+	// check if the vector instruction has been enabled
+	VLEIB  $0, $0xF, V16
+	VLGVB  $0, V16, R1
+	CMPBNE R1, $0xF, novmsl
+	MOVB   $1, ret+0(FP)    // have vx
+	RET
+
+novmsl:
+	MOVB $0, ret+0(FP) // no vx
+	RET
diff --git a/src/ssl/test/runner/poly1305/vectors_test.go b/src/ssl/test/runner/poly1305/vectors_test.go
new file mode 100644
index 0000000..18d7ff8
--- /dev/null
+++ b/src/ssl/test/runner/poly1305/vectors_test.go
@@ -0,0 +1,2943 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package poly1305
+
+var testData = [...]test{
+	// edge cases
+	{
+		// see https://go-review.googlesource.com/#/c/30101/
+		key: "3b3a29e93b213a5c5c3b3b053a3a8c0d00000000000000000000000000000000",
+		tag: "6dc18b8c344cd79927118bbe84b7f314",
+		in:  "81d8b2e46a25213b58fee4213a2a28e921c12a9632516d3b73272727becf2129",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "04000000000000000000000000000000", // (2¹³⁰-1) % (2¹³⁰-5)
+		in: "ffffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "faffffffffffffffffffffffffffffff", // (2¹³⁰-6) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (2¹³⁰-5) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "f9ffffffffffffffffffffffffffffff", // (2*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (2*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "f8ffffffffffffffffffffffffffffff", // (3*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (3*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "f7ffffffffffffffffffffffffffffff", // (4*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (4*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "f3ffffffffffffffffffffffffffffff", // (8*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (8*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "ebffffffffffffffffffffffffffffff", // (16*(2¹³⁰-6)) % (2¹³⁰-5)
+		in: "faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"faffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	{
+		key: "0100000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000", // (16*(2¹³⁰-5)) % (2¹³⁰-5)
+		in: "fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"fbffffffffffffffffffffffffffffff" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000" +
+			"00000000000000000000000000000000",
+	},
+	// original smoke tests
+	{
+		key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035",
+		tag: "a6f745008f81c916a20dcc74eef2b2f0",
+		in:  "48656c6c6f20776f726c6421",
+	},
+	{
+		key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035",
+		tag: "49ec78090e481ec6c26b33b91ccc0307",
+		in:  "0000000000000000000000000000000000000000000000000000000000000000",
+	},
+	{
+		key: "746869732069732033322d62797465206b657920666f7220506f6c7931333035",
+		tag: "da84bcab02676c38cdb015604274c2aa",
+		in: "000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000",
+	},
+	{
+		key: "0000000000000000000000000000000000000000000000000000000000000000",
+		tag: "00000000000000000000000000000000",
+		in: "000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000000000" +
+			"000000000000000000000000000000000000000000000000000000",
+	},
+	// randomly generated
+	{
+		key: "52fdfc072182654f163f5f0f9a621d729566c74d10037c4d7bbb0407d1e2c649",
+		tag: "9566c74d10037c4d7bbb0407d1e2c649",
+		in:  "",
+	},
+	{
+		key: "81855ad8681d0d86d1e91e00167939cb6694d2c422acd208a0072939487f6999",
+		tag: "eaa270caaa12faa39b797374a4b8a420",
+		in:  "eb",
+	},
+	{
+		key: "9d18a44784045d87f3c67cf22746e995af5a25367951baa2ff6cd471c483f15f",
+		tag: "dbea66e1da48a8f822887c6162c2acf1",
+		in:  "b90b",
+	},
+	{
+		key: "adb37c5821b6d95526a41a9504680b4e7c8b763a1b1d49d4955c848621632525",
+		tag: "6ac09aaa88c32ee95a7198376f16abdb",
+		in:  "3fec73",
+	},
+	{
+		key: "8dd7a9e28bf921119c160f0702448615bbda08313f6a8eb668d20bf505987592",
+		tag: "b1443487f97fe340b04a74719ed4de68",
+		in:  "1e668a5b",
+	},
+	{
+		key: "df2c7fc4844592d2572bcd0668d2d6c52f5054e2d0836bf84c7174cb7476364c",
+		tag: "7463be0f9d99a5348039e4afcbf4019c",
+		in:  "c3dbd968b0",
+	},
+	{
+		key: "f7172ed85794bb358b0c3b525da1786f9fff094279db1944ebd7a19d0f7bbacb",
+		tag: "2edaee3bcf303fd05609e131716f8157",
+		in:  "e0255aa5b7d4",
+	},
+	{
+		key: "4bec40f84c892b9bffd43629b0223beea5f4f74391f445d15afd4294040374f6",
+		tag: "965f18767420c1d94a4ef657e8d15e1e",
+		in:  "924b98cbf8713f",
+	},
+	{
+		key: "8d962d7c8d019192c24224e2cafccae3a61fb586b14323a6bc8f9e7df1d92933",
+		tag: "2bf4a33287dd6d87e1ed4282f7342b6a",
+		in:  "3ff993933bea6f5b",
+	},
+	{
+		key: "3af6de0374366c4719e43a1b067d89bc7f01f1f573981659a44ff17a4c7215a3",
+		tag: "c5e987b60373a48893c5af30acf2471f",
+		in:  "b539eb1e5849c6077d",
+	},
+	{
+		key: "bb5722f5717a289a266f97647981998ebea89c0b4b373970115e82ed6f4125c8",
+		tag: "19f0f640b309d168ea1b480e6a4faee5",
+		in:  "fa7311e4d7defa922daa",
+	},
+	{
+		key: "e7786667f7e936cd4f24abf7df866baa56038367ad6145de1ee8f4a8b0993ebd",
+		tag: "de75e5565d97834b9fa84ad568d31359",
+		in:  "f8883a0ad8be9c3978b048",
+	},
+	{
+		key: "83e56a156a8de563afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e",
+		tag: "de184a5a9b826aa203c5c017986d6690",
+		in:  "4da6430105220d0b29688b73",
+	},
+	{
+		key: "4b8ea0f3ca9936e8461f10d77c96ea80a7a665f606f6a63b7f3dfd2567c18979",
+		tag: "7478f18d9684905aa5d1a34ee67e4c84",
+		in:  "e4d60f26686d9bf2fb26c901ff",
+	},
+	{
+		key: "354cde1607ee294b39f32b7c7822ba64f84ab43ca0c6e6b91c1fd3be89904341",
+		tag: "3b2008a9c52b5308f5538b789ab5506f",
+		in:  "79d3af4491a369012db92d184fc3",
+	},
+	{
+		key: "9d1734ff5716428953bb6865fcf92b0c3a17c9028be9914eb7649c6c93478009",
+		tag: "71c8e76a67a505b7370b562ba15ba032",
+		in:  "79d1830356f2a54c3deab2a4b4475d",
+	},
+	{
+		key: "63afbe8fb56987c77f5818526f1814be823350eab13935f31d84484517e924ae",
+		tag: "1dc895f74f866bdb3edf6c4430829c1c",
+		in:  "f78ae151c00755925836b7075885650c",
+	},
+	{
+		key: "30ec29a3703934bf50a28da102975deda77e758579ea3dfe4136abf752b3b827",
+		tag: "afca2b3ba7b0e1a928001966883e9b16",
+		in: "1d03e944b3c9db366b75045f8efd69d22ae5411947cb553d7694267aef4e" +
+			"bcea406b32d6108bd68584f57e37caac6e33feaa3263a399437024ba9c9b" +
+			"14678a274f01a910ae295f6efbfe5f5abf44ccde263b5606633e2bf0006f" +
+			"28295d7d39069f01a239c4365854c3af7f6b41d631f92b9a8d12f4125732" +
+			"5fff332f7576b0620556304a3e3eae14c28d0cea39d2901a52720da85ca1" +
+			"e4b38eaf3f",
+	},
+	{
+		key: "44c6c6ef8362f2f54fc00e09d6fc25640854c15dfcacaa8a2cecce5a3aba53ab",
+		tag: "6f2a09aa76c9b76774e31ec02dcf7991",
+		in: "705b18db94b4d338a5143e63408d8724b0cf3fae17a3f79be1072fb63c35" +
+			"d6042c4160f38ee9e2a9f3fb4ffb0019b454d522b5ffa17604193fb89667" +
+			"10a7960732ca52cf53c3f520c889b79bf504cfb57c7601232d589baccea9" +
+			"d6e263e25c27741d3f6c62cbbb15d9afbcbf7f7da41ab0408e3969c2e2cd" +
+			"cf233438bf1774ace7709a4f091e9a83fdeae0ec55eb233a9b5394cb3c78" +
+			"56b546d313c8a3b4c1c0e05447f4ba370eb36dbcfdec90b302dcdc3b9ef5" +
+			"22e2a6f1ed0afec1f8e20faabedf6b162e717d3a748a58677a0c56348f89" +
+			"21a266b11d0f334c62fe52ba53af19779cb2948b6570ffa0b773963c130a" +
+			"d797ddea",
+	},
+	{
+		key: "fe4e3ad29b5125210f0ef1c314090f07c79a6f571c246f3e9ac0b7413ef110bd",
+		tag: "27381e3fc2a356103fb796f107d826e7",
+		in: "58b00ce73bff706f7ff4b6f44090a32711f3208e4e4b89cb5165ce64002c" +
+			"bd9c2887aa113df2468928d5a23b9ca740f80c9382d9c6034ad2960c7965" +
+			"03e1ce221725f50caf1fbfe831b10b7bf5b15c47a53dbf8e7dcafc9e1386" +
+			"47a4b44ed4bce964ed47f74aa594468ced323cb76f0d3fac476c9fb03fc9" +
+			"228fbae88fd580663a0454b68312207f0a3b584c62316492b49753b5d502" +
+			"7ce15a4f0a58250d8fb50e77f2bf4f0152e5d49435807f9d4b97be6fb779" +
+			"70466a5626fe33408cf9e88e2c797408a32d29416baf206a329cfffd4a75" +
+			"e498320982c85aad70384859c05a4b13a1d5b2f5bfef5a6ed92da482caa9" +
+			"568e5b6fe9d8a9ddd9eb09277b92cef9046efa18500944cbe800a0b1527e" +
+			"a6",
+	},
+	{
+		key: "4729a861d2f6497a3235c37f4192779ec1d96b3b1c5424fce0b727b03072e641",
+		tag: "0173965669fb9de88d38a827a0271271",
+		in: "5a761f03abaa40abc9448fddeb2191d945c04767af847afd0edb5d8857b7" +
+			"99acb18e4affabe3037ffe7fa68aa8af5e39cc416e734d373c5ebebc9cdc" +
+			"c595bcce3c7bd3d8df93fab7e125ddebafe65a31bd5d41e2d2ce9c2b1789" +
+			"2f0fea1931a290220777a93143dfdcbfa68406e877073ff08834e197a403" +
+			"4aa48afa3f85b8a62708caebbac880b5b89b93da53810164402104e648b6" +
+			"226a1b78021851f5d9ac0f313a89ddfc454c5f8f72ac89b38b19f53784c1" +
+			"9e9beac03c875a27db029de37ae37a42318813487685929359ca8c5eb94e" +
+			"152dc1af42ea3d1676c1bdd19ab8e2925c6daee4de5ef9f9dcf08dfcbd02" +
+			"b80809398585928a0f7de50be1a6dc1d5768e8537988fddce562e9b948c9" +
+			"18bba3e933e5c400cde5e60c5ead6fc7ae77ba1d259b188a4b21c86fbc23" +
+			"d728b45347eada650af24c56d0800a8691332088a805bd55c446e25eb075" +
+			"90bafcccbec6177536401d9a2b7f512b54bfc9d00532adf5aaa7c3a96bc5" +
+			"9b489f77d9042c5bce26b163defde5ee6a0fbb3e9346cef81f0ae9515ef3" +
+			"0fa47a364e75aea9e111d596e685a591121966e031650d510354aa845580" +
+			"ff560760fd36514ca197c875f1d02d9216eba7627e2398322eb5cf43d72b" +
+			"d2e5b887d4630fb8d4747ead6eb82acd1c5b078143ee26a586ad23139d50" +
+			"41723470bf24a865837c",
+	},
+	{
+		key: "9123461c41f5ff99aa99ce24eb4d788576e3336e65491622558fdf297b9fa007",
+		tag: "1eb0cdad9237905250d30a24fe172a34",
+		in: "864bafd7cd4ca1b2fb5766ab431a032b72b9a7e937ed648d0801f29055d3" +
+			"090d2463718254f9442483c7b98b938045da519843854b0ed3f7ba951a49" +
+			"3f321f0966603022c1dfc579b99ed9d20d573ad53171c8fef7f1f4e4613b" +
+			"b365b2ebb44f0ffb6907136385cdc838f0bdd4c812f042577410aca008c2" +
+			"afbc4c79c62572e20f8ed94ee62b4de7aa1cc84c887e1f7c31e927dfe52a" +
+			"5f8f46627eb5d3a4fe16fafce23623e196c9dfff7fbaff4ffe94f4589733" +
+			"e563e19d3045aad3e226488ac02cca4291aed169dce5039d6ab00e40f67a" +
+			"ab29332de1448b35507c7c8a09c4db07105dc31003620405da3b2169f5a9" +
+			"10c9d0096e5e3ef1b570680746acd0cc7760331b663138d6d342b051b5df" +
+			"410637cf7aee9b0c8c10a8f9980630f34ce001c0ab7ac65e502d39b216cb" +
+			"c50e73a32eaf936401e2506bd8b82c30d346bc4b2fa319f245a8657ec122" +
+			"eaf4ad5425c249ee160e17b95541c2aee5df820ac85de3f8e784870fd87a" +
+			"36cc0d163833df636613a9cc947437b6592835b9f6f4f8c0e70dbeebae7b" +
+			"14cdb9bc41033aa5baf40d45e24d72eac4a28e3ca030c9937ab8409a7cbf" +
+			"05ae21f97425254543d94d115900b90ae703b97d9856d2441d14ba49a677" +
+			"de8b18cb454b99ddd9daa7ccbb7500dae4e2e5df8cf3859ebddada6745fb" +
+			"a6a04c5c37c7ca35036f11732ce8bc27b48868611fc73c82a491bfabd7a1" +
+			"9df50fdc78a55dbbc2fd37f9296566557fab885b039f30e706f0cd5961e1" +
+			"9b642221db44a69497b8ad99408fe1e037c68bf7c5e5de1d2c68192348ec" +
+			"1189fb2e36973cef09ff14be23922801f6eaee41409158b45f2dec82d17c" +
+			"aaba160cd6",
+	},
+	{
+		key: "40ff73495fe4a05ce1202ca7287ed3235b95e69f571fa5e656aaa51fae1ebdd7",
+		tag: "2e619d8ea81b77484e4fddeb29844e4b",
+		in: "aa6269c2ec7f4057b33593bc84888c970fd528d4a99a1eab9d2420134537" +
+			"cd6d02282e0981e140232a4a87383a21d1845c408ad757043813032a0bd5" +
+			"a30dcca6e3aa2df04715d879279a96879a4f3690ac2025a60c7db15e0501" +
+			"ebc34b734355fe4a059bd3899d920e95f1c46d432f9b08e64d7f9b38965d" +
+			"5a77a7ac183c3833e1a3425ead69d4f975012fd1a49ed832f69e6e9c63b4" +
+			"53ec049c9e7a5cf944232d10353f64434abae060f6506ad3fdb1f4415b0a" +
+			"f9ce8c208bc20ee526741539fa3203c77ecba410fd6718f227e0b430f9bc" +
+			"b049a3d38540dc222969120ce80f2007cd42a708a721aa29987b45d4e428" +
+			"811984ecad349cc35dd93515cefe0b002cee5e71c47935e281ebfc4b8b65" +
+			"2b69ccb092e55a20f1b9f97d046296124621928739a86671cc180152b953" +
+			"e3bf9d19f825c3dd54ae1688e49efb5efe65dcdad34bc860010e7c8c997c" +
+			"d5f9e320ca7d39d4ba801a175b1c76f057832f3f36d7d893e216e4c7bbdb" +
+			"548d0ba48449330027368b34f9c69776b4591532da1c5be68ef4eebe8cb8" +
+			"fa7dc5483fb70c2c896334cb1f9cb5dfe044fa086197ff5dfd02f2ba3884" +
+			"c53dd718c8560da743a8e9d4aeae20ccef002d82ca352592b8d8f2a8df3b" +
+			"0c35f15b9b370dca80d4ca8e9a133eb52094f2dd5c08731f52315d828846" +
+			"e37df68fd10658b480f2ac84233633957e688e924ffe3713b52c76fd8a56" +
+			"da8bb07daa8eb4eb8f7334f99256e2766a4109150eed424f0f743543cdea" +
+			"66e5baaa03edc918e8305bb19fc0c6b4ddb4aa3886cb5090940fc6d4cabe" +
+			"2153809e4ed60a0e2af07f1b2a6bb5a6017a578a27cbdc20a1759f76b088" +
+			"9a83ce25ce3ca91a4eb5c2f8580819da04d02c41770c01746de44f3db6e3" +
+			"402e7873db7635516e87b33e4b412ba3df68544920f5ea27ec097710954f" +
+			"42158bdba66d4814c064b4112538676095467c89ba98e6a543758d7093a4" +
+			"94df",
+	},
+	{
+		key: "5cc36d09c7a6472a41f29c380a987b1ecdcf84765f4e5d3ceefc1c02181f570f",
+		tag: "0d57b8cbea8090df0541354673dcb4e0",
+		in: "44fcd629f08dc1ef53c9ae0d8869fe67fdc7a2c67b425f13c5be8d9f630c" +
+			"1d063c02fd75cf64c1aec9d2e2ef6e6431d5f5ad0489078dc61f46494dcc" +
+			"f403dad7f094170d2c3e29c198b0f341e284c4be8fa60c1a478d6bd55dd2" +
+			"c04dad86d2053d5d25b014e3d8b64322cdcb5004faa46cfa2d6ad2ff933b" +
+			"c3bd9a5a74660af3d048a9a43634c0250427d9a6219197a3f3633f841753" +
+			"ba7c27f3619f387b6b1a6cb9c1dc227674aa020724d137da2cb87b1615d5" +
+			"12974fa4747dd1e17d02c9462a44fec150ca3a8f99cc1e4953365e429956" +
+			"5e108535b1f62e1d4ba18e17a52164418bfd1a933f7fb3a126c860830a87" +
+			"293d9271da736e4398c1e37fb75c4bf02786e1faf4b610cd1377fbb9ae18" +
+			"0655a0abefbad700c09473469f1eca5a66d53fa3dc7cd3e7c3b0411d7e14" +
+			"5f96eb9654ab94913dda503a50f9e773842f4d2a5faa60869bf365830511" +
+			"f2ededd03e0a73000edb60c9a29a5f5e194cf3b5667a694690384599d116" +
+			"f8d2fd93b2aed55b7d44b5b054f3f38e788e4fdf36e591568c41d1052cad" +
+			"0fcb68ca4c4bf5090d57df9db6f0d91dd8b11b804f331adb7efb087a5604" +
+			"e9e22b4d54db40bcbc6e272ff5eaddfc1471459e59f0554c58251342134a" +
+			"8daaef1498069ba581ef1da2510be92843487a4eb8111c79a6f0195fc38a" +
+			"d6aee93c1df2b5897eaa38ad8f47ab2fe0e3aa3e6accbfd4c16d46843318" +
+			"5fc61c861b96ca65e34d31f24d6f56ee85092314a4d7656205c15322f1c9" +
+			"7613c079eae292ba966e10d1e700164e518b243f424c46f9ea63db1c2c34" +
+			"b512c403c128ee19030a6226517b805a072512a5e4cd274b7fd1fa23f830" +
+			"058208ff1a063b41039c74036b5b3da8b1a0b93135a710352da0f6c31203" +
+			"a09d1f2329651bb3ab3984ab591f2247e71cd44835e7a1a1b66d8595f7ae" +
+			"f9bf39d1417d2d31ea3599d405ff4b5999a86f52f3259b452909b57937d8" +
+			"5364d6c23deb4f14e0d9fcee9184df5994fdc11f045c025c8d561adb0e7d" +
+			"fd4748fd4b20f84e53322471a410cdb3fd88e48b2e7eb7ae5dae994cb5ea" +
+			"e3eaf21cf9005db560d6d22e4d9b97d7e9e488751afcd72aa176c0fcde93" +
+			"16f676fd527d9c42105b851639f09ea70533d26fc60cbeb4b76ed554fc99" +
+			"177620b28ca6f56a716f8cb384",
+	},
+	{
+		key: "811c3e356e7c793acf114c624dc86ace38e67bff2a60e5b2a6c20723c1b9f003",
+		tag: "c6e59044cefc43ee681c3eed872d02b3",
+		in: "e115b304c023792448794546a2474f04294d7a616215e5dd6c40a65bb6ed" +
+			"b508c3680b14c176c327fdfb1ee21962c0006b7deb4e5de87db21989d13c" +
+			"3ab0462d5d2a52ef4ca0d366ae06a314f50e3a21d9247f814037798cc5e1" +
+			"0a63de027477decdeb8a8e0c279299272490106ddf8683126f60d35772c6" +
+			"dfc744b0adbfd5dcf118c4f2b06cfaf077881d733a5e643b7c46976647d1" +
+			"c1d3f8f6237c6218fa86fb47080b1f7966137667bd6661660c43b75b6339" +
+			"0b514bbe491aa46b524bde1c5b7456255fb214c3f74907b7ce1cba94210b" +
+			"78b5e68f049fcb002b96a5d38d59df6e977d587abb42d0972d5f3ffc898b" +
+			"3cbec26f104255761aee1b8a232d703585dd276ee1f43c8cd7e92a993eb1" +
+			"5107d02f59ba75f8dd1442ee37786ddb902deb88dd0ebdbf229fb25a9dca" +
+			"86d0ce46a278a45f5517bff2c049cc959a227dcdd3aca677e96ce84390e9" +
+			"b9a28e0988777331847a59f1225b027a66c1421422683dd6081af95e16f2" +
+			"48ab03da494112449ce7bdace6c988292f95699bb5e4d9c8d250aa28a6df" +
+			"44c0c265156deb27e9476a0a4af44f34bdf631b4af1146afe34ea988fc95" +
+			"3e71fc21ce60b3962313000fe46d757109281f6e55bc950200d0834ceb5c" +
+			"41553afd12576f3fbb9a8e05883ccc51c9a1269b6d8e9d27123dce5d0bd6" +
+			"db649c6fea06b4e4e9dea8d2d17709dc50ae8aa38231fd409e9580e255fe" +
+			"2bf59e6e1b6e310610ea4881206262be76120d6c97db969e003947f08bad" +
+			"8fa731f149397c47d2c964e84f090e77e19046277e18cd8917c48a776c9d" +
+			"e627b6656203b522c60e97cc61914621c564243913ae643f1c9c9e0ad00a" +
+			"14f66eaa45844229ecc35abb2637317ae5d5e338c68691bea8fa1fd469b7" +
+			"b54d0fccd730c1284ec7e6fccdec800b8fa67e6e55ac574f1e53a65ab976" +
+			"4c218a404184793cc9892308e296b334c85f7097edc16927c2451c4cd7e5" +
+			"3f239aa4f4c83241bde178f692898b1ece2dbcb19a97e64c4710326528f2" +
+			"4b099d0b674bd614fad307d9b9440adab32117f0f15b1450277b00eb366e" +
+			"0260fca84c1d27e50a1116d2ce16c8f5eb212c77c1a84425744ea3195edb" +
+			"b54c970b77e090b644942d43fe8c4546a158bad7620217a40e34b9bb84d1" +
+			"89eff32b20ef3f015714dbb1f150015d6eeb84cbccbd3fffa63bde89",
+	},
+	{
+		key: "f33691f5db2dea41e1e608af3ff39f3a6988dba204ce1b09214475ae0ea864b8",
+		tag: "6e50e70411201378c8d67857d7b631d2",
+		in: "439bc9ea10db4d2b08c7fcf2e8bd89fa9844f8061d462e28f174489e7514" +
+			"0f84e842040141cc59ce38f9551850cfbdfac2d75337d155090d70d0d930" +
+			"04340bdfe60062f17c53f3c9005b9995a0feb49f6bef8eaff80f4feb7ef3" +
+			"f2181733a4b43b6ac43a5130a73a9b3c2cbc93bd296cd5f48c9df022b6c8" +
+			"2bb752bc21e3d8379be31328aa32edc11efc8a4b4b3f370ee8c870cd281d" +
+			"614e6bc2c0a5ca303bc48696a3bd574ee34738de4c4c29910f8feb7557bf" +
+			"ffcfe7428b4703144bd6d7fe5b3f5de748918553df5453b3c6001696f3de" +
+			"0137e454aadf30cedfb6be36b0b908a38409f1a2dc202fc285610765e4c8" +
+			"6414692bf4bde20ed899e97727b7ea1d95d7c621717c560f1d260ab3624e" +
+			"d6168d77c483dd5ce0d234049017795f2e5a7569d7ad323c50a5b1170337" +
+			"4174a9977026c20cd52c10b72f14e0569a684a3dcf2ccbc148fd3db506e2" +
+			"8d24f6c55544cb3980a36e86747adc89ebad78d1630618d113fa445f8625" +
+			"b583cd7be33913c30c419d047cf3baf40fd05219a1fcec717b87a65fa022" +
+			"1a3aa8143062d77588168019454240ae3d37640996f2967810459bc658df" +
+			"e556de4d07263dc3d9158ec242008226d1c6aea7f0846e12ce2d316e80da" +
+			"522343264ec9451ec23aaaa367d640faad4af3d44d6d86544ade34c93518" +
+			"2843f6b4d1c934996778affa9ee962e7dfef5e70d933d4309f0f343e9606" +
+			"1b91b11ac380a9675e17a96099fe411bedc28a298cd78d5496e28fbbd4f5" +
+			"b0a27735d1144348e22be5b75724d8f125e99c4cb4e9c3a1f0b4e9da5146" +
+			"e6afaa33d02fda74bf58a8badee2b634b989c01755afa6ab20ee494c6ae4" +
+			"c2c6f17af6b53b61d2947d83a18eb3b8a1612aad5d3ea7e8e35f325c9168" +
+			"ac490f22cb713ddb61fbd96011c5849ac8e2fcd42db820349bdf9157dcc0" +
+			"0d9f9ed9c099b10c7194d48b623b0df43759734b2a2e5f8a35e7192bf9a0" +
+			"03dcb9d16a54bd84d922f85b6021b28aacc5264fe9e83deb48f18f864cbd" +
+			"367eb163d39c45b0eb907311a2a4b09fb26109088df782ce031b02f3caff" +
+			"d2dbe25b1cbde9f35ba7c47292a4fd49e7def7a28824f3dfda259a86c3de" +
+			"59257c255c712686ee47d128a55c7b9e8c546035eab7e2da420f32ed5c94" +
+			"bc12a34dc68eb99257a7ea03b69d6c760b0681fa24e4ca97b7c377182ab5" +
+			"fee30a278b08c44c988a8f925af2997883111c750d176b432735868208f4" +
+			"0de7137331b544f2d28040a3581d195e82811c945c3f9fde68fc21b36a44" +
+			"e1cfa2d8eb625f3102461539b3f13c660936a5ddb29a0ae791fbf52c2f69" +
+			"7bd334653f3605b362d91cd78569b41dbd09b2a5892440b5097fa08d0b4b" +
+			"291fc5b934585dd8d5adc80d573fdd194b2eae26dfc49f5e51c1f1607d7e" +
+			"87740702f244bf39ca1d52423e0ae84891dfdf4f43ef984c7a5f293a2007" +
+			"a1e00e39c757f064518953f55621f955986f63",
+	},
+	{
+		key: "d115b6ac998a65b48b3dae5977abaf985258d3d1cfe1616cec3d6a77f7a75785",
+		tag: "b431c9318ec2769fc8ee8f5fc3c079c3",
+		in: "7e7eb43839a6d7616b8a7b1fb7144817904342a9bd34167051162941a6b1" +
+			"b85db5e587f76e4a53211755d5ab29c11822d7711a97b3f1ff5b21f2485d" +
+			"9c86241fb56cdd6796245d3112df11ad9a7344db44d09934c4efb280ed65" +
+			"80cfcafb5c97a32993cbbf4917183e0b7bb38f2ce2479c28e1d39f673962" +
+			"17a7010448dfd39a4e7f406c8bd2d804f993bb410fffa4eb57518a531ecf" +
+			"259a8af068230acb826d9ffc20ee0fc43885221a321e3928971bb28615f0" +
+			"d9f099f5b68a80503a910fdba0bc643c60b64837900be38770b6b30c362c" +
+			"4580722b5dbb1b9c8cd02a18fd7b5661d2c4d28aa941c50af6655c826690" +
+			"37312fbf9f1cf4adb0b9400532755011b40e8252bd0e3c7a22efb0ef9122" +
+			"1e04b4aa8316d4a4ffeaa11909d38cc264650e7ca416835ded0953f39e29" +
+			"b01d3a33bba454760fb0a96d9fe50b3e42c95271e57840380d1fd39a375b" +
+			"3e5513a31a4b80a2dad8731d4fd1ced5ff61e1fbe8ff3ff90a277e6b5631" +
+			"f99f046c4c3c66158554f61af2ede73aede97e94b1d1f129aaadf9b53548" +
+			"553cc2304103e245b77701f134d94d2a3658f2b41108c5a519c2c8f450db" +
+			"027824f1c0ab94010589a4139ff521938b4f0c7bf0986585f535b6e292e5" +
+			"b3ded23bf81cec17c8420fe67a449e508864e4cbb7eaf335975668f013e9" +
+			"da70b33bd52a72094a8f03762ea7440ce9fcd10e251837cfc9ccc1a8cc47" +
+			"0c67379f6a32f16cf70ea8c19d1a67779a9b2d2b379665e0e908a88b26e7" +
+			"8c9f94f17acefa6d5feb70a7095e0297c53e091cf98df132a23a5ce5aa72" +
+			"59f1154b92e079f0b6f95d2a38aa5d62a2fd97c12ee7b085e57cc4652863" +
+			"8defacc1e70c3aceab82a9fa04e6aa70f5fbfd19de075bee4e3aac4a87d0" +
+			"ad0226a463a554816f1ebac08f30f4c3a93fa85d79b92f0da06348b4f008" +
+			"880fac2df0f768d8f9d082f5a747afb0f62eb29c89d926de9fc491921474" +
+			"1d8647c67d57ac55f94751389ee466bbd44dbe186f2f38abbc61a0425613" +
+			"e9b6a64e6bcb45a2e2bb783b9103483643d5610a7e2dcdb10b5d78423285" +
+			"506b42a99b00a4fb7b619b4526bb4ec78299dd01ad894fde2f053e18c55b" +
+			"6047f86333f2690c2cb8e87d9834ab8a5e339aa346e4d9952ed62dc083e3" +
+			"b11a823a67f23fec099a033f127ebe8626a89fa1a5a6b3520aa0d215a8e7" +
+			"dea3af37907686c16521739a95d6c532cc259c497bf397fceaea49cd46b9" +
+			"ad5c1b39a36fdd2f0d2225fef1b6ca2bb73fe604646c10ba4c572ab13a26" +
+			"559ededc98f5a34c874cc25621e65ba4852529b5a4e9c1b2bf8e1a8f8ff0" +
+			"5a31095b84696c6381eb9ad37ac0db184fe5fccf3554e514946a33cabe6f" +
+			"4d617b549d28ad1cc4642dac96e0215ee1596481600d3619e8f45e2c9ae1" +
+			"da834d44aca216bba0efef6254503ca90339f2d7ca508b2722d50c08def8" +
+			"a736590fa44855cd9eb9979c743783aa26e633696739f2ae25ff7b72ceb2" +
+			"4dff4455b85bbd675c8cb71ad18386dc58c371bdf37b4b3875b98a9423ff" +
+			"3becfc0d0ba2aacab3ee7683cb3b345095fefcaca5751ca793da63c89428",
+	},
+	{
+		key: "f3717306b9729be998cdb2c9d856306c5ae3d89da2cdcef12f86f6110c98d873",
+		tag: "907dba0f4849c7cf4570b5128b5f31d5",
+		in: "079572187d4559f24d8e48dc366441acf226a4db79e214ec3ee288acc349" +
+			"887e2e377419bcafa377d0151497b52e4d9cf2a02b0fc91ad9516482bdf6" +
+			"eccd1497954b53241bfb0bc5c04cc45045c6251f23a510060fee32721872" +
+			"bbc95cd8d400dff00bcac2ecce6229c7d73d8f85ed5a87afdccf6dedd299" +
+			"2d5c7b5b8090c47c737ded036ff0e9aedf02a2242fd9820be618b9601e73" +
+			"d3ba5d8f1ae9805cfd2306251704bc74e3546997f109f1dfae20c03ff31f" +
+			"17564769aa49f01233c9c4b79f90fa3d1433d18cdc497914046ad77d2792" +
+			"2588a7d0e61d4258d7d80cdab8503e3111ddca22cf7f39c1f80f1e16a68d" +
+			"9e21db8b53dd316dfa4233cb453a39a90101c60efc08514a3057db007e96" +
+			"507745bd4a0764ed8717a250bffb5fd1ea58474bdfb5b869681939693926" +
+			"40d832a3387ed4ac9cdab0d2af8fcb51b86e4d927097f1e79b5af96574ec" +
+			"d59d0dd150a0208978c41de28ad6cadf72a49279cffd6dc281c640f2e294" +
+			"4cde49a13ed390da1dd92e3011ce0f4a0863375a9db3f67fca1e3b8288a0" +
+			"78611161d7cb668ecdb932e1ff3733982c8c460eeeff2bca46c96e8a02cf" +
+			"b55d770940de556373a4dd676e3a0dd66f1280c8cb77a85136b3f003fab4" +
+			"887dad548de7bfe6488ae55e7a71da4097db03900d4b94e776a939530328" +
+			"83492da900b2a6c3e73d7a6f12ee30c9dd06cc34e5a3893976eb1de5864d" +
+			"32e792ac02e68d052d9d0cfc7cfb40b77728422f6c26cf68987c6b40fcfe" +
+			"9d660abc657360eb129de11bd70af5eb8fe350af2c27a6ece2cdf81b94c8" +
+			"0e68e8c51106497cfa5171236efe2d71d76b5dff3352af9b407dc5aab60f" +
+			"46b5683646f5b28732b7c750d351a08a507243d8e437cc4bef13a3edaa20" +
+			"5fc4e9968b4e563fa0dc965ba20b8e48bc188a321b16d3213bed69647512" +
+			"7a20afc1a3680ef261df6d37b017dee05cfc3a42e4130216e5540cf715c4" +
+			"e638d7d615c50bef576eeb19b3b15b2c2b454dfcef2b18161a143ddf52fc" +
+			"8e88fa71cbe34c92cd4b5a0adc81e5c33e11d2721bc1b95a9e693ac3cabc" +
+			"490889a8a42bf7e22375b679e8598c8faef22a006ed2da8ab1c08aaed2f5" +
+			"6d6f26649036335c0881bfec1e3a5346335c3b3707ee92173f1a7a3305c2" +
+			"933f78e995da8f1df64daf12b81ce23c8813c27fd4551103dc33561c2e80" +
+			"45b6b6770fa03498fd359a104884699d628020173edbcc4398b977e456e4" +
+			"885964840466176a490e7c513ba5d66090277c1ab1632a995a54f555a452" +
+			"1170a000507865b6650730aa6d6050a55959102836fff3d37e4773340e59" +
+			"2e56951ff9652519de4421d9c5b63edbeb30a3852a1ea110a9a29721aee3" +
+			"23d5a306de1624cecc87badc47aa87f489635d2fb60bff62ba67f5257999" +
+			"6af0a1f1a6fbcd8704e119196fcc289a6db6a4170a2cae31a1d30744b702" +
+			"2536d1526d41659c2dcc8b39c26aecfc0f8a707136d81b2827a158fd7386" +
+			"a537514471c213a8c859016748e0264cf3fbde10f40c620840ec4df99432" +
+			"e2b9e1e368e33f126ec40c572e841c2618d49d4eb098b9533b1f4ae00b46" +
+			"8d15de8c8ab6d0b650e599576f2bd90a124c9c6a0f911fd1bd8253bac272" +
+			"942cbdf8864f3747ff7f09d8a5a9d8599be7ee1744e5f1faf3e526cd2a06" +
+			"b157527272af9d38565957c9ce663c295766c0e0e464971c6282b70d4c0c" +
+			"1fb3b69856b34c089ad2b2c745f5a033cee1429c5b855581ee285278893c" +
+			"43a5968d9c28384b7abe8d072ba69089c938685cb1eab461f05314ad6d06" +
+			"eaa58512f8738bde35b7b15ef359dd2e8753cb1ed6",
+	},
+	{
+		key: "9772c1a4b74cbf53586e5df04369b35f1fdca390565872251bc6844bc81bda88",
+		tag: "68eb7fc459ecc3be819485001ab438dc",
+		in: "e115cc2f33e367cb85c01a914b3a512404ad6a98b5b0c3a211d4bffd5802" +
+			"ee43b3fb07451c74524ec8b4eddbb41ca33dd6e49791875d716a44bec97b" +
+			"7c2d4546616939ffa3b1ab9b8ba1d1a637e7c985cc922606caa0453085e3" +
+			"5f2fe0bd2de129d1d1856ade975a3281a62965927d8bb695e54514e69558" +
+			"89361a2a00a1b24e62bda78d0b71a0d40147016fcdaf1a702331dda8e678" +
+			"d8f476dcc91698da1688c610ec0cb1d9b8fbcd45dfde6d1503ba60a01337" +
+			"ae5b2f5c854a82c3087779babd2e522dd92f4718cd9f8c649ac226745ca2" +
+			"fa1696442764758f67cd926369578ae87612790dc56ed9cda935281a490e" +
+			"5c984950ec7a4e930520d273a69da4ed3a330e532508e26f942961fed0e3" +
+			"efeed52a7b96250d723155aa39a8ae85131c255c32bf406b647de1a37fba" +
+			"dc61e302bb5b70adec4505ee66b3a1d1b7bfe9c58b11e53ad556d56e5807" +
+			"017bb30b71be94e8f86aaf1496e8b8d6db75ec0afbe1cd336c23963c745d" +
+			"7b4ba1787ceb30728f1762b46f6eaad5064c8029d29b86266b87f93142a2" +
+			"74f519f3281d8c1cb43c23eb184ae41f3f625cf624b05a48d73cd7783fdf" +
+			"14954a03ec1a930e9a954424eff030e3f15357de4c19983f484619a0e9e2" +
+			"b67221cf965e9aa8d8926595c793adfe0181050df8b845ce648a66df532f" +
+			"78b10c83ecc86374a4f8abf8edcc303654bafd3dcc7de9c77a0a9d1d98fb" +
+			"121534b47d16f75b55fdc2a5e2e6799f8a2f8000d4292282e56863ae422a" +
+			"5779900ad6881b78946e750d7777f33f2f013a75c19615632c0e40b98338" +
+			"1e9b8d35a26abe30242c45662eebb157e6d7a8a5519de60268ac289b8295" +
+			"5d4feb47b9eef6da65031c6f52c2c4f5baa36fce3618b6a331f1e8bdd621" +
+			"48954fcf0846afeeb0a6cadb495c909a7fe671b021d5b0b4669961052187" +
+			"d01b67d44218471bfb04c1a3d82bf7b776208013fc8adabaefb11719f7a7" +
+			"e6cb0b92d4cc39b403ceb56bd806cbdcc9ee75362ab4aaeb760e170fdc6a" +
+			"23c038d45f465d8ec8519af8b0aad2eb5fae2972c603ed35ff8e46644803" +
+			"fc042ff8044540280766e35d8aaddcaa81e7c0c7eba28674f710492924c6" +
+			"1743da4d241e12b0c519910d4e31de332c2672ea77c9a3d5c60cd78a35d7" +
+			"924fda105b6f0a7cc11523157982418405be0bacf554b6398aeb9a1a3b12" +
+			"fe411c09e9bfb66416a47dd51cbd29abf8fbbd264dd57ba21a388c7e19e8" +
+			"12e66768b2584ad8471bef36245881fc04a22d9900a246668592ca35cfc3" +
+			"a8faf77da494df65f7d5c3daa129b7c98cef57e0826dee394eb927b3d6b3" +
+			"a3c42fa2576dcc6efd1259b6819da9544c82728276b324a36121a519aee5" +
+			"ae850738a44349cdec1220a6a933808aee44ba48ce46ec8fb7d897bd9e6b" +
+			"c4c325a27d1b457eb6be5c1806cd301c5d874d2e863fb0a01cbd3e1f5b0f" +
+			"8e0c771fca0c0b14042a7b0f3ae6264294a82212119b73821dcfbbfd85bb" +
+			"625b6f75e4dc0ee0292ab4f17daf1d507e6c97364260480d406bd43b7d8e" +
+			"8c2f26672a916321b482d5fa7166e282bfeed9b3598c8f8c19d2f8c8b98d" +
+			"f24c2500c8ad41cd6ed3f2835737916d846f1a6406cda1125ed7740fe301" +
+			"d1144559b7c95fa407599ae40a795226513153f86c9b8abe7d8aa6963c99" +
+			"5646ec586cbf20a03a698cc0681b7bd333402d00fa8e15cb32300b5a24ea" +
+			"316c5e1df67de78891846cb9183a4b112c3bcc17bcaa5fecd6c1dbbf6ef8" +
+			"272d9269e7f0ba9f17050a6aa5f11cb28874360396ab647941f2c9a85cb0" +
+			"6a969919b16997b0827af8f909c614545f1ad638ebb23109f6bab6b49b22" +
+			"b2285cabbb998b3e1bf42771b4d4e52330b224e5a1d63169ec85fe1c7dd2" +
+			"46dbafa6138448420f463d547a41c2b26026d4621b854bc7786ab3a0a93a" +
+			"e5390dd840f2454028b7c3bb87680f04f084089bbc8786ee42cf06904d01" +
+			"7e405144d2fae141599e2babe71abfbe7644fb25ec8a8a44a8928ff77a59" +
+			"a3e235de6bd7c7b803cf3cf60435e473e3315f02d7292b1c3f5a19c93646" +
+			"3cc4ccd6b24961083756f86ffa107322c5c7dd8d2e4ca0466f6725e8a35b" +
+			"574f0439f34ca52a393b2f017d2503ba2018fb4a0991fddc1949832d370a" +
+			"27c42e",
+	},
+	{
+		key: "d18a328b63a1d0f34e987682fe6ca3d48b4834b4312a17e99b3d88827b8d2238",
+		tag: "938b43b80cb3935e39b21dd8ba133cf8",
+		in: "bc2b0baf92580ee6c5efe640f2a029a791a3c77bec459be74cbc30931508" +
+			"d9f312c3a0944212831cbe4fc92e8f107f2f750c91bcc09f7624fa9a09b4" +
+			"9b7712cf5d619ea9da100fc23068ae2f4e353047e3956b215884bdb12235" +
+			"3f06b8ee98f36c3212493d61ae9ce151cd0453f3075b18a12d7d73da3de7" +
+			"dc2d98376cfb420069ca8148c511ca6bbae57572394a3c615a6fefb30c5f" +
+			"d727f964b4065ac9ee252bdd2bcae3e70162fe0e8069974e073f0a093d45" +
+			"be52d7de16a8f5f65c548aa6525822ffb00dc642530fedf355f7188ef017" +
+			"56384760c80afb61ad903d10119a7d615ec4fbdc79c490160bdeaf200915" +
+			"e405f2a921a2380c0ab9d2ac1e4fdc8ec4b907368c004458598efac13dc7" +
+			"2751e7faded538e3dc8b16590cac9b7ec294da0ad53e22cb9c05d8ef494f" +
+			"a04f6ab7c843c867fbe3cf1b4eb146d65339b0b03392259f12627a8e98e8" +
+			"0f4896c30b8ecd210acb2365539a872541921dcd8e1e54caf4936dfc7e1f" +
+			"68f3bbce61d325b447a8cce7f0fcad28494f2e47dae46b136594b5dfca7a" +
+			"bdafd6856f91496c05b21079aa55aa8c41628220a2cf0cdd755893375b7b" +
+			"b13d914c9a1d1db4a18f8fa36c55e52d0342352052032fb62d32fcd51cb1" +
+			"ac46f44b06e682db5d96d583cda03b966c650c03ae53542e8da1066b6884" +
+			"4a7e2280c664415e413f270b1fdcfbb40b9daa6131d071ee7eb1553dc5b1" +
+			"a50677971223dc316d2d326d57cbd529c88698facdca425e2d5c6b10d7ae" +
+			"cae28b8890aa44ede9b9193dbe8d1d8aa1fa580ca384b57eadcbefc96dd8" +
+			"bfccbe3b855a96f1fd4913035f817b75954ef1827c7718aab24d353e41cb" +
+			"a73748e14e0c2750d5b6a9752125708cc7ee7a498c7fbadf4186e7f8fa93" +
+			"bfdf281a49400f877621651b8ba87edda5231e80b758564e75139b61b1a9" +
+			"9fb9ec694f928ab1f47c6c4287bd4182d1b2be053380616e98da06f3ef57" +
+			"b570ade17c51da1d602b6ebc5a638ebde30d99bf4f91d0e01557c7dcd8f7" +
+			"9e5120143c935fc699eb5616ccd3cac56b5f8a53ed9e6c47ba896bfefe71" +
+			"2004ad908c12cf6d954b83bec8fb0e641cc261ff8f542b86e62d90e227f2" +
+			"a5bd59c9d390c0dd857f6da2b7624787a0bb31908bae84896890b283da61" +
+			"d8ec4f56eea38b22b438d6374b42243f9c1d94288874e53ab90c554cc1f1" +
+			"d736acde67aff55007fd4b3becc4d0f3ddd96f10dc75255cb0327aa47076" +
+			"2b3a3a656e33c87b02a682658b6cd2a75d9c0462803c9bbffa51441501a0" +
+			"3a2fbb2344aa13d27ffb9e98704ea6720b6a9992e53449688cd74d0648fa" +
+			"e8e776b0ea6bf048b2ec05341e5948cab0af015328b284ae7bd89a5f763c" +
+			"eaf5ca3e647a9f5bff7197e4d357e4359fa5fe30709545453149be510e3b" +
+			"ff86beeba5110c79c0215fbe9ac9339a8ac7d41f7488588ab14ac657aaf7" +
+			"d5c03a353932bbb2b261f0e83f3526c5e8e0c2348a10ab4eed6ecdcf9014" +
+			"7550abcb0a722f257e01d38bad47cdd5a64eef43ef4e741bf50da275720a" +
+			"0aee47adfc5cd2534b911dc269197c3c396820b303f6941e3fd85b5ed21d" +
+			"6d8136745c3eeb9f36b1f226434e334dc94be8a5606079cb7643136aacd2" +
+			"da9c38b2eb7e2b898bd8632003767bf0c87d00a3c2fcee48bbbcdd949af3" +
+			"3455128216709df25879b0ce894ac4f121dfca6b8c7865002b828696641d" +
+			"14ffc59924fbda50866fded0afaea545c8008c564a3a0b023f519a9980ea" +
+			"d541d91d1c07a739fd02286ea5660e473f80494236a68e84ea31aad71348" +
+			"e45055ded69c39941e31d51df257a4d0b0d8f025dbedee093f2b91795bc1" +
+			"533dc472020769a157a187abd6d8d52e1693e2ef56b2212759d0c0120e54" +
+			"c425d0084fdb3925e296dd6cdd8e677043a90674904057d88ebdea5998aa" +
+			"03562a790adecc4399352df43e5179cf8c584d95ef8e4b37295946b1d37f" +
+			"faf4b3b7b98869184e42ea8b304fe1059f180ff83d14a0861ca7c0682c34" +
+			"b48a70df8653bd8d9a26f9489e1271fa44e41b392e648d0e619ecdad2c53" +
+			"952094802eeb70ade4ffe096e3049867de93a824217e31364b18204e9681" +
+			"dd8e84ae2678aad155b238f59dd9bf9ce07e97183a690b2a46a8f3624843" +
+			"5b2f713e7d8dcda4dea1e3c4cf9692dda082322c51f7bb1f63d92aa987ec" +
+			"cf1355a043e21a7b8d60a2b97f18487f6fff4c77df92dbfdc9837540c518" +
+			"9fd9585731bc6e726a34ca21154b0499522c9d1016953dd0fa2eb6a92b6d" +
+			"14d6e3da5c12fabe92bd639e253983fc91041091791643",
+	},
+	{
+		key: "46e8eb27acfdc8f4be622d8741c7bc414464c149e21da97ab4afbf3e07b98b0e",
+		tag: "56b5f49be824c7a19b19faabf0787a87",
+		in: "ced52b76c057872a60107194b432cf04b7be05e65209045d2952ea0284d8" +
+			"3e2ed5a15cfdc58071204573c18ab03765b4d5e63a601419e039c42075b2" +
+			"7ebb2827de9c6233d6632e6d3db9140bdb4a9291d53f33734c2dc8e24df9" +
+			"0764dc10e0d321d20fdf659bfa2a81bc9e04fd0f83448143276647c08bfa" +
+			"dcfe3bc23898eda655c9353693ed7b022f43eefa23c21db7660c5029ca64" +
+			"a6085d93029ea6c43197356f56b7624d4819f5008d053357d981ffbe7f40" +
+			"96d6c55d8417002d36189b04bbb2c637339d90f4910a400833a8d422d88d" +
+			"c816c1636e8d9f7f926c244a28d9e0a956cec11e81d0fd81d4b2b5d4904a" +
+			"d1a5f55b5ec078dcb5c2bc1112bbfd5efc8c2577fe6d9872a985ee129e5b" +
+			"953e9cebf28cf23c6f9c6a5e09cb09ab586c6a50e4389cd3110777591d7f" +
+			"0608a3fd95b99f6ba03984fb0e13c6bbbde3668c59f2f2b69d7caadffa94" +
+			"6f67e725d56280e59e66dca025a18d4616e81abd9801835bd94485bb2025" +
+			"dee81fba440005b181ee81dc1d7796cbec92e4ec1c9016c8e8073cf281ce" +
+			"f749993f09a618a4671d58b476feffa454600f82955c591882715148a826" +
+			"586f68bb50059914dce1c1c85e5e3951647c9964ec9316005209a58baeb5" +
+			"2c6d01e6b4c275c0050a7e2bdc52133e433b050a700b556d4314e5c041d1" +
+			"93ee47f47adc971aed1b63259dd5cd4f95854a71a947eae3d3d12d0d7b52" +
+			"c6cd2fef2d2e892607a9681d73ac3236fad21ee30a4f857010bc95c00d5f" +
+			"6f0c6b3fe50cd6452be6eec4f5f01542dc2cb5e2db1f52224f11348fe2a0" +
+			"5d1e5885f1317f2d06ce2813dc4c723008e836a2ee95d0aac66855fe4c3b" +
+			"1b2e02ba0700be759b1ef1c2a3123ee4ccf9200d8d4de5e0d503f04c2053" +
+			"66393d1e91b648392ca28389d976aa618b4796acbfe8aa356ecdce1f7786" +
+			"bf09af226bb9402317b6fa319bbb9248d8ce00b1f49f066c69d4df93266b" +
+			"938342cd7fd4b07c320c2409ef72d8a57c21d0c6d6d493f7ca94d01b9852" +
+			"e4fca6a9291e9060154bc38af6c86932645f53914709fc90e11db56ec471" +
+			"6d600ee6452041248ea8244f79534f793bfc1f2020855d817cb4ca3c48ea" +
+			"7f6441ce9af9bda61936c226d810086c04a35e8654fdc30d4b35701adccc" +
+			"016d5895b2121ba4066e44d694f6371d97911786edb73dc3020ba186a01f" +
+			"ee3dd6036c0e205a8d05979bad228fd12c0fd2fded6c7f1e4c11354d266e" +
+			"d9c2f706269c43cd90504997d93a17b39b10dab0ff083ab3bd06540ce612" +
+			"d08f46ce75a16ef330525737410a0d98fb3d484968f9c12edcaf50103fdc" +
+			"c14128ea4ad6c30b56247eab28197fe617e5f88afa5cbe003c63d423647a" +
+			"d3042626fafd2084a0582ff1b1efdb5baa162662048019546234e2f6b6a1" +
+			"d8bb971114aae41df7795b4f3598f2af9e8921a9aadc7fab6c780aaa32a3" +
+			"84865a4ccb02351dbc55ec92a3152d1e66ec9d478be5dca17b4a131b4a0d" +
+			"3d4420fc6123fef80fd56ca266407d58a7880d6b7e5ce2b6bdc9a3721071" +
+			"7feec573d83c83a2e3f7d4023f2f68e785cde728fdbf5054060e4c89faa6" +
+			"1c9dd10524a08811d15c627b3b4ada549a3fa1d8dd77c005daaf2addeb10" +
+			"0abf694da8dd692f113965cd6366a5a7b0c17e1f2a320243e2c90b01418e" +
+			"22426d0401a2c8fd02cb3129a14fdfa6cbcaa1f1c2f17706e9ac374a3458" +
+			"777761e986ee4c358d26f8e420d33230d198fd86704e77298dd4c40c5205" +
+			"7566ac0cd92993b21937c3a3b4a8b89110a97cf38c781ad758bdc28f3565" +
+			"60cf3acbedfa8e05b396d226ef619746e8e4fa84c8e00a7f0e6d652808c8" +
+			"9c9b123d9bd802624cfa949eb68af85ca459b9aa85b81dbc0b630856cb9d" +
+			"7e18cdc96b3c069a006dd5b716e218a5ed1f580be3e3ccf0083017607902" +
+			"a7967a02d0a439e7c54b3b7ca4cc9d94a7754efba0bb5e192e8d1a6e7c79" +
+			"4aa59e410869b21009d9443204213f7bceb880ccf1f61edb6a67c395a361" +
+			"ff14144262b4d90c0e715dbefce92339ff704cc4065d56118624a7e429e4" +
+			"cadf0b9d2e7ffc4eb31c6078474a5265beba0774209c79bf81a930b302bd" +
+			"0f142534a6ae402da6d355a010d8c82dc379ea16d49b9d859a7de4db6e62" +
+			"40f6976ae0f47bc583b327df7ec88f5bd68f713b5d53796e72e28c29e843" +
+			"6c64cd411d335623ff4f5d167f3c7b8cba411e82f03714662425c8e1bc1e" +
+			"fbf435d28df541a914a55317de0ded8c744a1c3a6e047590244b207bcdcb" +
+			"f4bd1f9f81210deddd629192c58e6fd73e83812f084ef52f21c67bea98ee" +
+			"17554437d9642e2e",
+	},
+	{
+		key: "b41210e5ef845bd5a8128455c4e67b533e3e2b19dffc1fb754caa528c234d6a0",
+		tag: "72c9534aec8c1d883eef899f04e1c65e",
+		in: "7eeca180bb20d99635e36b9208221b2b8ef073fbf5a57f5190e19cb86c49" +
+			"89b0e8150d22ec3aaf56f6ed9cb6720284d13a4b0a34cd3d7f7fc7089326" +
+			"6d1893fa4185269fb806677ff490aec8f889896fca50d6c80d295875b1d5" +
+			"4a779b6d49305360b31011b48537157d0f323ff4e865d46fba6bd23a06c1" +
+			"46878cf9404360d325432312ff08ce495edca63a3c93c44d79c050e3f1de" +
+			"4b6ca5fedbbd43dbdef9ceb26d440a59c7e0be3a8e461c4f15b6b1e1dc36" +
+			"a71fc723ad593fb903e83d0804ce497fc49bfc6b6a602b9dc6e9891010b1" +
+			"4ca066cb1c68044c1ad837c638076dd3708078509cba49fdc54922cdf5d7" +
+			"715fb43e9b5a5942cb8950eade143577bc9dcedde58d51deddc70075e452" +
+			"bbceab1e95b5d003eb96bea69687faa6d50d9c605769cb4287b5d9924dd6" +
+			"8881c699abaa6f93e41dac7639cdbbbd0259099a3ed096f482a1fa322b15" +
+			"ffc379812c74e09e95f1bd3706347eac421fe56895e738a47fcd3e118773" +
+			"c3a7e7e264cc7ff5a53a80e436df058265dab9756fdf6913786a47e98bbc" +
+			"411052d58ffec9ee948e28cbaadaae471c5d828eaf3b3c87d3bfd495477b" +
+			"403da54f1418a15ace0d4d0df68f6a8f2b0457b127d5eae1f45ae055afa1" +
+			"8f058d5dd7eea559de3ae9378ca53f7d6dc9a9465ea1f945295f16ee0404" +
+			"7fc9dd3deda8ee32631d7af70c20edc1e12c5f8abd2e78f43dbd4cd6407f" +
+			"038efab144a24ea8a090a7ba3e6499345a60106220c2959a388e1a73d070" +
+			"1d854bfaaa86165a5aee934b615ac7f45da7c43a1e8f74613917ed10dcd2" +
+			"27e4b070414412e77851db5bc053e5f502bb4e2b2645bca074c18643e814" +
+			"4caeccb58be49ea9a552913c0616382c899635eea79a166988c206b9aaa0" +
+			"977c7ced89c4c7aaeaa8fb89b38030c44530a97187fda592b088198b63a5" +
+			"2dfad59a0a4c1aadf812bdf1881924e8b51b8fd4dbca8e73b2986b3ab484" +
+			"171e9d0cbb08be40ae60de8818bd7f400191b42c7b3200c27643f06720a7" +
+			"e0a17441f34131629388ac43955b78c31ea6602a70dd665f872e7669e865" +
+			"f6f40e634e8772d747608cd3a570e1726eb1ddca64f08582b022bb026eda" +
+			"6a913dc83f174ce3c18b9fc0503d3ac74e2fe45691d6dfb4af8c86d752a1" +
+			"6d6664fab4de08afe8858392fcc35cb9ea82fc42c42d48c0c0556267ea0d" +
+			"cc19b10f05e0318c4488ffe704b5036908f5cb938eebd3163503acaa874f" +
+			"592d945448fbeb93a877a26a72306a36e181745ba300afdc30cb7986919f" +
+			"3dbdc5c47ef1fa052a9e4aeeda3955f61ce2f30a0593a81dbaffebac5a49" +
+			"e5a8d1308352701d1ca9e620a67a89abdf5f0f8b1a0acfde5819981d4b77" +
+			"58799c0fe41030b86754837712af821c315301aa8dd50d1387b9fb92ee63" +
+			"10777e08229edd54e5e86b086ac281bd321082ef46ce298a6211aaa3aa4f" +
+			"6e55b5a4641220ec94cca73087760da1b1ac3e0da3f438214e691aa184b0" +
+			"535950b715a64d11485940dcaa3f72e0aa521002b1443f5e7880e2a85b83" +
+			"40d32db0fc4c4702e10f0fa24a35da9307850e945f608ad34d6cfdf6f2b9" +
+			"ff4f6b8e9eb5a883546578e2ff3cc5787322e4384640f42dc5bd05f432d9" +
+			"610dcf7c06cdf34762dd2a5e805e24aee8cebb3b4db9e4d1471da995bba9" +
+			"a72cf59ea8a040671b1d8ce24a3dce4fc86d2df85c8ab5e1eb2b0567c186" +
+			"4fb464f48c3ca72c7df2749542ed4d4be51b63769012ce3d06356856b2a4" +
+			"24995a2429a156ad93bc79c705e7b163149ce53a42c34a19680dfe4fd0f7" +
+			"fce38c30dffe9da9bc941d131f435c1398f8284a230e9d6e3992710074c3" +
+			"881d03aa309a9edd0fde7a39c33f6455dfcc5ae3fa20ea0e0d6549a43536" +
+			"b4cd8a2991a135b7d7a4265fb840318813091274414108f13fe191db7774" +
+			"6a5f4270f6d51a29ff523954f84cb76131d4abee79161dcbd97dc1ef24cf" +
+			"db1fade057dddee00a1e0de0db1afaeed1b535f7bb402afa3b297551fd14" +
+			"8c8f3e05f1351d3a8ee2948daaf14e7fc448c4670c906ae076eac5a7c656" +
+			"fd5f9cd937b91e26c9e5adb43c138f8d65e447b0022a524e059f879c6e27" +
+			"4ff7e671f75717233aae70853d5bd7bbb41b43c47bb08d6dc2f54f9ec606" +
+			"9487d1267add72403d01552a3d138abab9ca8a0d2dc32439759aa5695f70" +
+			"1a17d28dfb85850fdb55fddadcdde4d220e4b05821e5736d346e7dc9c945" +
+			"72743366488b1de8975184771361894b6520e3407c5c2e38473430969e35" +
+			"b106024da8618665d58c9d084824a28991a33658d6ec702139e01b65b7d0" +
+			"cc537a644caeee880657803d95f5f67816948d5ab362922f8ffbd531473e" +
+			"b0ff8fde2afc37a4abfa28dbed0be1b3d4ed48a1d02358e8403905d33b12" +
+			"3066e7a9fe2491ee9eb24fc9de7dbd322c8ddbc5ebcd0d92cd102ebac96b" +
+			"90e2fd784fd6d4b699304df23b17d963080a013794322690456be525c071" +
+			"b78fcd2d1148026e44ff14c4d0f942cd44d2b3263f4a93b79ec7a618b4b0" +
+			"d77ae7a1f6e6c7c7e2f498b825bf1954df348bae45ae1d7c87b6787f1212" +
+			"60c9a724429a4a2491ef989f65acfdc72fa717486dcf1984905218e11cc3" +
+			"970a09d71061e6df751f100abfbf",
+	},
+	{
+		key: "d9b0dc303188756312c12d08488c29f43a72e78714560fe476703c1d9d3e20c1",
+		tag: "6b9782f2a09b59653aa448348a49291b",
+		in: "dbde1820035997dc8a8ff3015b4e0674e7ce7bf0c2d994b7977f2d91b49b" +
+			"f200995040daeb1218a0f4307b6b8211913992b070d321bdb947b4ba5017" +
+			"a0885e7e5502710a75cbbcb56d49e1bdc2bc2afa5a0e83851162dec41340" +
+			"bafc41c5e11fcbf4ea2ac45bc57def4742281bbf734777f83c9ae1ea3d5e" +
+			"d42380230570f59c40d5dd9a2d89b75fa3c92664f12a274d965ed8de79a8" +
+			"b37f3763939ad21d1703ad794f617c8b32b20cc4dd7c1b7f969a65e1bafa" +
+			"f6c43f30c9eba256f10201910e2cc31a9b13a46ad29257024ef8f2ee29b2" +
+			"ee63cc5b6230ab9f87cd5cb534f4b0bb08a790466e0d57b849fffa1ed21b" +
+			"fb0b27804e3ff9df7bebf14e100cf91691a493e53870abfad6321f6711c5" +
+			"0fbcf1f0b2c1e5231d6c0a08e710525176355f6f82bedc1f787f0d3cb41f" +
+			"a11e91ebf9f4cbae46035a371232d63ef0d8bda0355af8cd0a2f7d1327d8" +
+			"0ab769ea0f1da0f76ec99cc737b5ce84675fa8a9ac0c98342bb82b5848bf" +
+			"656d35327ea01a1b09d84ab974c307511af68a30cd6978b529a8f58c68a5" +
+			"9d476062ace8897ec0d1a90d5d167e29ebaa6f46d93d697760c8771417ce" +
+			"94c0f3698985a98702833d1b68641b811840ca3d935386dbd4600fbc81c8" +
+			"728c4fd0e4588be739a048f03bd4ac651ceecd7e2fb120fe7190011f957f" +
+			"cbbfdc025f1ca0b356208db8cad87fcd53c5d3a30a7c2a48140ccd4cdb49" +
+			"f3961cef742caedd1e848bf3cacafb0da030416bf3177877aa0bc5f9d1cc" +
+			"41fafcb829d5e3ace9394028683d712552579e024084a6b855830ad9f567" +
+			"ff58f05d3ec263eddd6f56adec378f167e8dabbeaf7d0a9e65c71660314d" +
+			"6c8d54beeca2711113fbc32a2ff8c0daa8373278d10085d2a0660ad53f4e" +
+			"1ade74a483be180180acf9e9ad3ea5bdd9162ccd69599163a451c6837d5e" +
+			"a5e115bd9a560f395128ea002ee739009a44fa46078b18959933fb6e866f" +
+			"eb4612a56ce93b1affcb95fccaa18d71a148582ba1412a5daa07404fcb39" +
+			"c3cb4a2519cc506c1172c6c326016ae2e5410f6a438569f35a50d45cbf3c" +
+			"c46188651aa22c257858f60649cee8c05c75953ce49358dfe5980445fce9" +
+			"614ccd16d333ad236e29d204691ca0bf46f29da954bcaae52e41016556d2" +
+			"f4cae1d37565bcbe84de1b49f344d0200478a38187da29c155cc98184d9d" +
+			"33dca088d70054e0fce321f7a90c48a14963d0ace2b4e7a24b21c14a5e67" +
+			"1994fe1f7d22d1135d4df9268dd18d323fde3603288735626a5449582d35" +
+			"30e2c2225414e05a8c7b987c873a82e272a5d83e59b90f3d7264631d6ad0" +
+			"4a0cf3b5e96596a66ed5bfbc24ab6e4870aeec0acbad2cc5affaee06de32" +
+			"dca06f175bf763cf8e7fdf95941a177e934f0078be7dbaa4c9b6f5c16b4a" +
+			"5607bab5d56144a6ba3c7d9a084b8d1f4b24b6f9754ed207b230d3a2cc26" +
+			"259ccc725e1f8a44c4df8143e13edb5ebf073e2c9d2da5f1562df4feece2" +
+			"f6480987f093f642eb7afa3aa92dce2a8b60bb925cd2d11cf6c2ae7d2153" +
+			"1a9c8f068d71d0e682023932fe64e956a49347aed22b21084c4a84480491" +
+			"244ac6b337b6d12d5551ad5684766c68bacca62bdcafab6603c81bdbd8e6" +
+			"80d9d8b3825eaea4df023142e840f98ee251466a0422d810a54726a9f03a" +
+			"7e0afeb0043e60e2ba4908f951d2e87fcbc372096f2a9f4f2a95ad5faede" +
+			"3796b11ecf4401c3ee3d268bd8c46476c61e0ffc5c43c0f3c58c79e20f75" +
+			"520c102aa3c260972a870fc50f8841fa0553a9e30bf37ad282fb51b34adc" +
+			"7a933ca1691a8a706605ce0b906fdccbe954f8e5f2f63c42599a483c4be7" +
+			"3a041ef90ad930fe60e7e6d44bab29eebde5abb111e433447825c8a46ef7" +
+			"070d1f65862b30418efd93bfea9c2b601a994354a2ff1fc11c383e7bc555" +
+			"9e7546b8bf8d44358b1ce8cb63978dd194260e00a88a8fd17df06373aa80" +
+			"04a89172a6051bd5b8cea41bdaf3f23fc0612197f5573f3f72bce39c9f89" +
+			"faf3fb48d8ca918586d4feaea7e0f2a0d7a6afca096a081af462ea5318cc" +
+			"898a9cc09e8258a837559570cbd5eb901e8c0e04ee88ba31c81a76b000b8" +
+			"0e544feba576b3eb5272b53e46e96a0b35b9c759caadcec61444f8ec47c3" +
+			"45a1d2304e2708eeddfbfa75a98eab3493889047d690e84431d445407fdd" +
+			"99560c0bdd287e0944116f8ac62ab992ed3f1e2b415aea784b03c6904795" +
+			"f4326ff60bc839615f2894570dc9c27cf928ef192047528a1a19ec990978" +
+			"3b0d1a13dd4baf4a19e49bf798975abe2ad167dd574b32b3d0c22aa4d9b5" +
+			"2761e8f56cf2100fe5a39fceae3d865f3724d4f299d07ff899fed6baf7fc" +
+			"eb7189357bf56cf94a6493e61301b43e3ed158cb9c7a0e615fd9888c2db0" +
+			"7f7689762f62ef6b3ad4125e06b07a422f5040c3aa8b8f205d68356c9225" +
+			"56fc4c976165fed9599daeb297498ecf744bf6c7dc5e30604c461ad99402" +
+			"2eea0fb6fe33f82a97b5c272fd24162a94b761ec7e52173e7bb42e88b343" +
+			"64f5fa2c141ed04a86b8d00fd9c25bf77a8dc3e63f5543331405be6bf421" +
+			"6a891089b316aa4f887cb4aff0dfb4e80c2ccd65ddd9daa74b17b4411c0f" +
+			"c849dc748d9b138279dcd9ebfc6e6759a53f5c28a41bb82107d71cc161fa" +
+			"81291a8290",
+	},
+	{
+		key: "fb70ae7ec12264ff9f51124da188e5b11dbf53cae2671363f6054b575b1ddcc1",
+		tag: "d9ab81fab28b3be96fa3331714e78c9a",
+		in: "c62edf20b1d53962b42386eb570b10378f9764421ecbd7c4802853332747" +
+			"19ff4c89c06005050fa9ba6579a844060eb7ece6c43bab520e683e0f36ba" +
+			"49cba259edc6ae35d41e0d7812a7d5edbe4d90cd5e0504d16f4c3f70d01f" +
+			"5a0313de55934b661ce1ec317968c2c4de60f45c66cded8c10565a1ca6d2" +
+			"3a84bf182df2fcb05956ed4d46b49fc0fe3bd23961d9466fde070341ce41" +
+			"bc6e148449360a31634fe10e91082d82def90d9da2c250ea72c58add2058" +
+			"d046b4392b78bc3af5b3936ed568733e8ad5672dabbfa3130a6a535ec73b" +
+			"da8e7223535f49f96cd35d56ed4792c5cb7076720d5461d96a2692b2ada5" +
+			"2be08fb7bad15d15a0108143790024f0f15f5adc275e783aa56b70844061" +
+			"e30952a040e4cb9650f2a010417812790105d8f58bd25d99b0db3cb16229" +
+			"3f6322e86cd5b0bb1505a7b998fb0f81d1e1915faca3c2c8ddea39115507" +
+			"80339430a7955521839deff5b301f3fad54edd5ebd2ac4ec9b1795cb4dc0" +
+			"e2eb62ebca8e886c3f1e507d10a0228c3027b472a7104b815f5ec8dae55e" +
+			"0783ff7ae9a3e6b99e381ad788206b135520cb870ba0cdbe876feea843b8" +
+			"5a82adc95a6d71c555f798da92b82daf0abfcdbc82ec30b1f12d78490b06" +
+			"7315735017a94ac150b44dfaace151896f873923310ffcd41e91bac04de6" +
+			"d70ea71565948c907ab21c4a23703fbbd2a8de6d3095f3d8f901538968e3" +
+			"60e7bfddb9d22036b1c23f4f5f1b2ee22623426a2d5de68c1e1a38e38e08" +
+			"e2b5670aac1edff69e9c73c2ca56cb69c709009ef1d541aff1fdb2b40c92" +
+			"9b87f162f394b76cdbba1f5605993e4dd9c312321d59b0aa5c6e33be1b10" +
+			"bfd00b92d4c02db064d0e4a98f2913c89051b0f0ead163deb5087b6466d9" +
+			"84f57553b0fa53850eaa142e072fd91802eb9f0d2eb7318dd620555e6ce1" +
+			"86706b866d41cf6ba81f100342faa14d801dc6f3d522db38fab17a879fcb" +
+			"b6acfe922163505bd23a6842f6ef6397ae5fb6e6016421998bd43b0142b0" +
+			"3ca3b16d6ccb7a47891c75c687d791a930b26aaa2e3412e7aa16e2cf1501" +
+			"7bf6df6d2e1c289af0d7ce03954a60c1dfcee5e4b3da51eb43ddd14faf59" +
+			"082005d0c8b104561f66c002ff426be60be769282fc5685cfd1968df1941" +
+			"73667e48e9ad681d35757f1199f1d93377bbad093c8cc3efa2bcb6ecb703" +
+			"694422772d15aaa58cab9e9ab277ed510f684114cc4a44ccadb3eb1c9a76" +
+			"d8619a9b7743106df6fb6f927ac49b22ae5bb9a9a4d231e340a2cd0e3282" +
+			"53f6d75df694826f60e4b3e758398793eaf73ef5d4b56cd1471e16400f40" +
+			"4a947e9737f4f874fe09a29ad799f4525156e3abbf0585c3c3c0a3744c86" +
+			"5d56db3d2ecba6bcbb1adcc8bf5f3b2a2d46d3eba18cda55201598a8112f" +
+			"d8f14e205f0e615f081b8ff6c5aa6669da776bfc7c34d5af4d0b26d0d819" +
+			"f6aacc53cf3c6653138b9a962acee9d6ea01d280c35bb1f05d1509238ccf" +
+			"004c5013167f804d1780d9f4ef9d45742fccac346b0472bde24ff5db9ae0" +
+			"16455a3c02256358fcd8e6a9aae94f8a37a1a3da58a889bbe3d295e16544" +
+			"2e580f59bdd31c92ffcab40c49c1cdbb4db1dd4882b66edc10fcb1704203" +
+			"c518c1d8d4c268588ce13fc38e0210aeb47d11d2603d4b3de5c6ff5e969b" +
+			"9d5904abb282b699bd04a6e9f1cb323679e30400d725aab128a032745dc0" +
+			"be05a46b02b34b93bff02523cd8498c021fc35a488f164a70ef1ceb873d9" +
+			"14a681d3a3a34cc76bfd5a547e2630d7741a284511bae5897d9f7a197fc2" +
+			"456af5c6cd7e1a93d3388c7a990b5feacd7749cf39fdecdc20adfdd540c6" +
+			"9d330195db7cc0d4555ea5f5356a3647e2265399f153c34ed1e217c5dafd" +
+			"c2c5dd3d566c332c7ddacb0d76ecd3a0ad505a4165443aa81b0f43cabfb4" +
+			"62942fe74a77c22b8f68a8b1a6d712d1e9b86e6a750005a3796ba1545396" +
+			"13170906d228dabf572ab969c762f8b296054f23d5d4a37bff64bf9cc46f" +
+			"43b491b41101256018376d487fe8097f1653a7a9e99e1ef2492600598fb0" +
+			"bbb7df8270be8b9106126d6f491f8b342a96ab95df6133e883d3db4c6a99" +
+			"402aeb58d371263a32dcf76d33c8904395b9cf0016fdfc15608eb43e20b0" +
+			"99cbe7455f7a76f69bba058ef96f83ae752587485657f89c7f26fde7fbeb" +
+			"a82ede581ee92821dc13b8202930aa58bd4f1c86f68926baca0d06fee642" +
+			"ea8c652d226af91a9638a0244f1a03c7ce56969b87cd5c1f86110d192e0b" +
+			"98dd979d74acca6c1956b1127d9a1f456053d17974081ed8ced0faa4293a" +
+			"319e5b25ba285c1151214f52c283e39c35af51c4572c8e395b7856697bfe" +
+			"dfc4145ab4ed0bdbe43ba509c06a196ae6bf30d7582550cb546c63b51833" +
+			"cb0dfff7196d83f6a1c6d6d712cce2ec1989fd9ff5a0a22ac5022b49d566" +
+			"58f196703e4809e7624fe7cfa6c13b378f5aac7e66e657ed7eaa942d1a00" +
+			"544a947199f24d736b8976ec2cfb563433c49ba131bd08b63636854219d4" +
+			"c45100c98e3092773ef492dd9210bfd8f54cfe2cddafcf5c05468d90e620" +
+			"0c2ef99d17fa6992cc45eff3072b7cfd51cabb07ea3019582c245b3ff758" +
+			"0302e88edc2c13fc43646ba34de37338568baa66ecff3accfebad88d143a" +
+			"fd1c3b09ae39c501e3f116af33b0b720d6c2baf5acd7f31220788b2f9017" +
+			"3ed7a51f400054e174d3b692273fcab263eb87bc38b1f486e707d399fe8d" +
+			"5a3f0a7ed4f5e443d477d1ab30bc0b312b7d85754cb886e9",
+	},
+	{
+		key: "f7e7affceb80a0127d9ce2f27693f447be80efc695d2e3ee9ca37c3f1b4120f4",
+		tag: "41c32ced08a16bb35ac8c23868f58ac9",
+		in: "5a3607fb98eaea52e4d642e98aa35719bfce5b7d7902950995f4a87c3dc6" +
+			"ad6238aadc71b7884318c2b93cd24139eed13d68773f901307a90189e272" +
+			"6471e4bf9e786b2e4cf144764f33c3ac3e66521f845f6f0688f09eaa227f" +
+			"e71033b0f74295f6ddb91fe741323f2b54f420cb9b774d4291b06219f1fb" +
+			"4410b55900425c5e6fcabec76a5c2424d637a1641db6f0f6cad564a36a91" +
+			"0f49894bfd598e91f38ceea65e8253c1284f210cf7b50a96e664e562f3cc" +
+			"01c4fc490fa6d4679fd63fbb3ed8995a8a05166b573e92d22ef4370c6aac" +
+			"74ae94c94177e5f71143c6f340efceefda679ae76f6ed7f26eaa4848a8de" +
+			"8c40894316efbb06400f9695b18ba279e8947c032a84a40ca647d9ace457" +
+			"6dd0082494d6bd7be4e7928e749c78110af8774a5d43e9c9479964e2fddc" +
+			"ee51146460eac734311225d08c60706e40f298a7cb97f369ef599be097ac" +
+			"3bf1c275497bbd68968a235fdf8a61bc7cfeef0fe451bb04e662ca39f34e" +
+			"a8e3acdd0befe9762f9eeb275c0cdd43c80fc91131d1e0e790020975ab65" +
+			"afbea81f303ebd86760821efb4cad7cc01fd6d6fd194ac5ffe7703d890d0" +
+			"169e21b444cdbaf691fc741a5d99bd47357c37785755fa72582ca4754a03" +
+			"b4def86ded39aa6d9eb3f38801077e6d17e3cee3fb57ae83f30c79c3cf29" +
+			"0e2739c6b7323612cec3a561ebeadb4faa642f150323aaa9d270658c907c" +
+			"4c1610a5e1834730c08be3379cf1abc50c30e2bf01ce903927c27d85e135" +
+			"3db9e216dda8860c45925e2bb791abe5c8281ee6d16607bdca87f60662dc" +
+			"bd6e20224e7f009a86db66fadd8e37e0a59559328385090c6953cd20bb61" +
+			"f28a734fb056714f5159977f18e5c5f11de75f7a00ba807e47a29e4da32d" +
+			"5c67ec76ce4d7b669b5e6ee17e1df7c673dd8a7c87fce665cda8adb9547d" +
+			"1dccbdbe7be44846b4b121b0bfa65e4ed530789510d79bc4477e50178060" +
+			"f2668ac8956f39ef422ecb0e4cf90b8ce508552eedeeefa6c7d1bccc077e" +
+			"8088bd7e0e6aaf0bda9f11c412c270ee2ad6912f9808f9344a4bb137bdac" +
+			"b5b9372b00b0de026a8f5d1fb13972e1290b5005689f7636c43aee2fd443" +
+			"93d390371ae573f0e064b2d7df552b9adf04bf173d71c621795b9fb503dc" +
+			"5e918536c6ad25ce4a76f70e6b752b6d44be321187269a19bcf33ec899ca" +
+			"40e88b4eb23217095a85057bf95d8a54812cae4a7d32e0c2966a21376110" +
+			"74c6c8c3dd45a553c43c675d23308709f91be0b235d0222aa5e1e1ce08f9" +
+			"c6b45ceb5b47bcd7d7b2d4380bcdbd6eced452d93e6d8cbe18123277889c" +
+			"7f86b15fb991364a501fbf5d8244f2e3332ea0ab49e833c6f765017a4006" +
+			"cc7cd1a0365945a8d8873cb21832b210c83e451c01ac949de2fb0f7a420e" +
+			"405bf64eb251c6f022181595d68174b91e503187d3b3f49b60c23e44ea40" +
+			"ca20311305b413047bb22e89672758b74d6bd1a06decf09e9556421087a4" +
+			"0c1d2c44c5fb13d4d9625581ac4ccef1a1b5eeb5689aac5c0291aebda276" +
+			"50daf9d4396a64d02c6d58bcbd609d9a0017880ae0cbaf02ad0f1fc8d1b3" +
+			"ec987ffe13102d77352690c9b761bf13ea0b3a8ebad4a0823817fcaab4d0" +
+			"9b0bf03486620761dc77a6ba007ba07153b17425c4026597473e78863cbf" +
+			"430c0e5e9b04a83ad11506b61b8d9be3aeb06b5114e0d53d4724863eba12" +
+			"4f3b974bdb0d02743520409910621cd730c97ca984fe2921c38055f83ee8" +
+			"c4611db92e52d8ea51d89203e89df7586c574df15f3a96ed5a10bf04cb27" +
+			"f9656b5b11cf35fd21360b029ab26e9a741c6b3e6357aa1a41de2cac6e85" +
+			"f9a49e3441e60a60e74f434e1b8cd4454b11962e5507ebf904e9d6c52a7d" +
+			"9722300517c434758fbd6191f4550108b143eb16c0b60094fdc29327492c" +
+			"18a3f36737e506fda2ae48cd48691533f525acfffb619d356bf8347a8bbb" +
+			"4babdc2ac866e497f192e65a694d620687cfb4f631fbd6ae5d20ac2e3a12" +
+			"4d85f9391a240b616d829ac2adceedf8f3451ee77e4835639b13c622ef8c" +
+			"48a181fc7598eacb419fa438d4046aa971942c86b36eb8e16eab67105783" +
+			"d27fc56f5b66f35451b2a407d4648a87ae70807e45bccf14983b3abcb198" +
+			"d661d562dfcb00ffc569ca967171746e4e36f839946bc7d2ea9a0eda85b5" +
+			"a5594f6a9c1b179f7230eaa7797a6aaf8628d67fd538050cf47aa654778c" +
+			"11dbdc149458c1ec2233c7ca5cb172356424eb79479b6a3eed1deb9f3278" +
+			"5282a1034ba165032b0d30733912e7cd775cdb7e0f2616b05d521dc407a2" +
+			"ae7dfcf46fbae30547b56f14dbb0ead11b3666666c45d345cd5dbfa200ae" +
+			"24d5d0b747cdc29dfe7d9029a3e8c94d205c0b78b56d5e18613b3169bd44" +
+			"1b3c31513528fe102f9bac588c400f29c515d59bbcb0725a62c2e5bfb32b" +
+			"5cf291d737e67f923080f52d8a79f2324e45a3bd051bd51bac2816c501af" +
+			"873b27f253ef9b92ba4d7a422e2fb26a35c1e99eca605acc10d2a60369d0" +
+			"1f52bca5850299a522b3aa126f470675fa2ec84793a31e9ac0d11beab08e" +
+			"2c66d989a1e1b89db8d11439ad0d0e79617eafe0160e88384f936c15eb15" +
+			"ece4ff00e1ba80b0f9fb7a7d6138bdf0bf48d5d2ad494deae0ccf448c4bd" +
+			"60f0788d3f2b76de8ad1456f7572bd0ffd27bc2836d704d95e9c0df34571" +
+			"9dab267dd805577fafda03b834dd225ad9714d2bd182b4103faa5975180f" +
+			"90d5d6cac1825a19b9d4c87cc825512ae9dbeb33d2759c990905050f960c" +
+			"db3eb364c15b593524c882902b2a1d7fe40ea3f54fb0202fd8821463c7e3" +
+			"4b02a1209ba0048a9805f0468a13e03d18009318ecd92042959be263a51a" +
+			"407f1e660632c4247419659a4e073a8e9cd4a226763a7daea464d5427270" +
+			"7efd053cb4efc0504602c4f63e7d247b55db2ce1c07138f585d16cec97a3" +
+			"0731d5aec2166cb4de41695feb76280cbae1af8a2e67c2d5a3ac5487ffe8" +
+			"640f308ace6137e83576b79d586b663122221c20aba7a6bf60f73958f436" +
+			"59f087f850ba6e2d7fd862249c5fa6b20e3e43d4f2aa10d4c9cebfcbdf02" +
+			"6b8d103e4f89b93dd8af172f421001c8b162bd6d0b847a58ac108b6d6cc4" +
+			"9c7a9ba069deee",
+	},
+	{
+		key: "e3d21f9674f72ae65661aebe726a8a6496dd3cc4b3319f797e75ccbc98125caa",
+		tag: "3c95668130de728d24f7bca0c91588bc",
+		in: "baaea2b4b4cbe9dbc4fa193c376271f40a9e216836dc35ac8012476e9abd" +
+			"43dac6b9ce67dc6815904e6c84a5730cea0f9b4c6900a04ae2f7344fd846" +
+			"58a99513ffb268c6899dfe98d605c11e7dc77de77b0d30986f3051754503" +
+			"7c26be7b719aa9ca1140cfdf4c586b7fe726a8bc403249396a11cfee0a6a" +
+			"f6c5e72259785cfd13c2897384fe527100170001ea19106aed38f7d5d9a7" +
+			"ad43f0b41451e19989192a46b4f9734a774b6304cb74feb7d83822044a24" +
+			"2e51d55c0b8318e0439493bd1a57cc13f6079166cabc46877d003dcd39b2" +
+			"c0b90f6b32fc77acf04a6c125e11b35d91e2b18401cd53df4aff804e3c67" +
+			"a8bb3894b27c6e9b0070b53a85aafab0c0a253f9cfd4d3cd3be52428385b" +
+			"24a3f9f71660ca2c38474d14a0309e2f400e2c21af6e379099283ff241d7" +
+			"51da5a96a8dcbfdc43b913b29cc8cf8020eebb4a67f5bed31f2e383f8656" +
+			"8c815ff172382b425e95902e80f5fc219eccb51b656d37b56660f749e5b1" +
+			"4976a23648680a472d02ba71476e0afb29a0e084984f4eac3befbf8dd802" +
+			"2b7dca4dadd18bbe58e49c49ce48a06a71557a9a620c51e2623f818e4d62" +
+			"c2564c7ba04595cc109685869b183faeff2ac7a65049fc57cb10fb01951e" +
+			"a525332782d691f9759ec2ecd68bebb9c7aece5d522a08ce7830be520db4" +
+			"c9d60a2e490eaa0c91e37b256a97f84b39fe3c77953748c3b86fd84e9547" +
+			"a298c049cb28b8c85d59548b8dce635d59487c9de615802d16a8adc4c0e7" +
+			"80f35b9f10588a431b39b499dca929ab9d225f26e5721820627fe62427fe" +
+			"06d5773a50878b6effe840dc55bd3ea0c35168f6b6a972d57e8f88c5993d" +
+			"1ae33e0b7e9459c123753b518c184de7aaf429df078c9a18a29af77c727b" +
+			"796f5c1a501fa8105ee873c4e78c907142eb19690638a182fddb413adb06" +
+			"d66db19c7f6f46dac582bd72a6347b4427a576eb769d233febaf7be8f768" +
+			"337273c12253924f15653f9f3602b783703a81454a1dd7a8772a9ab1eeb8" +
+			"51be33e0c6c0708f3cc2012cabe8e2f0c38e35372abe27bc148fc4e1054d" +
+			"9d151f80aec0232a3a92dd77928a3678ebd7d09ba7b4e1d83227257292c0" +
+			"b8bc4a76de36bff6c9deb383029afaf4f37d5b935dc080a18665545e4acc" +
+			"195da0b9545d8902408886204b64f8548b32d012e0cdc520c17d9fb3be97" +
+			"800c2e2b945cb09a75a0a49e5d4d81c4194d91e839333b2b9b9e34d588e4" +
+			"e20cc1e911ca0a1429fa70ff063f0090fd842f89dfc5cc44affcce4e1e1b" +
+			"8b11c612f66b074c03ac2a055fd8f51ac9ed4f2e624589ff5730721d077a" +
+			"fb4c19e43abf8cf3ffa698362be8be51e92c2c91a4a56be64d9ac6d3fbaf" +
+			"5536a24c7fd0adaf74ca84c508e5e8c8bf7d4254e0c44158bd26acdf3f64" +
+			"e78438b3aaff89ac9986cef1e3a88d5bf2016340367a1cacd01ec167ec6d" +
+			"185d93a2a220d718b43ce1d429d2cb598605660b030e51e8d75fdbdd5b8f" +
+			"8677675e196a40a88285b18b24c5d2d594bab3d457e6f9e503e38cd470a6" +
+			"9ff8037c9a0a0f110a434335d954fa856a3721e0edcfb14287c3dd9639ba" +
+			"4db32b7da0670dd0a872e468e3819741d0d4ecf0a4f7a011bbae1493c01e" +
+			"642757491189f8664be3ec6437c4f3c76abfb0276e44a4d28871d3487c2c" +
+			"ce2f230452cb06184bb8620919659a7ba0a3d5c12ec25678b03403715ee4" +
+			"acb6a53d281036d8f3a085143cf5ecc3a0c6c92129caa7ac1f645c7bb95e" +
+			"4f63da38dc319e2ccff4a9006f9b9b1a38c4c39f6dc686bb82d43fb9fce4" +
+			"0c767d3ff22f52c5f9900130c65bb6a9cc7408a777d49b70946665f4a733" +
+			"5099376b276a43dc9a6382bb2d40425f6481b1846148434c672b84dd7a20" +
+			"33deb5140d43ba39e04ffe83659b6deb48629e1abf51e68748deffb756a3" +
+			"ed9e0807506b248a024cd509f539f4161366547c62c72933584e851599b6" +
+			"82ec16f1d79e9c6a01cff6f51ba7f46b67cdca09f3ab8496322b990a6116" +
+			"8d7574854a1cb1cb8f30a303dbd13a095df56dbb940dd16ce79879cd2d73" +
+			"80a419842fa1b34da668286de4c1ff5917b7aaa64713c349dc8f855d04ae" +
+			"de9a3a4d0739dfc36510b1e7bb1695418164285c44631b4b1a7c5798ecb2" +
+			"d976c1a3679a827bf0e8c662567e402bcc1354222036ad5959a6f0b8508c" +
+			"6a8c7d4a63e7dde154d778fc80a011592771d55801c7e1297b00b77f80d6" +
+			"314ebd1f5b3057398d1943599897cfabb65e7568d8fbdfcbecfd4b8a83ca" +
+			"0a7bed08ab9a656424831e0d7718c15727af7c83b2ef5eb5684aa044eca2" +
+			"ba896811246766248b20a325094a4b4159f9cde1ee349be6dc3c9a190453" +
+			"0349212a9537f65ae333c288753cd2bef6c5beb2f4164168d965a2c0fb9c" +
+			"c8c73d9e776e23d53ddcfb83bb7dfe2a1b8c781280f449d6f310faf8b53e" +
+			"89e6a611d6d3f42f2aaed5259730d149b3e7dabdc9f865bc1555374738c8" +
+			"456abe112e9628fb31efc2ecdc972da05987aafce728ccaed246cfcdf518" +
+			"3fe5dae528bbfb99d33194167e0f84d462d3d0da83e92227cf57922c7956" +
+			"4fe44648d87c69ad708e797972c44c4a5183fd5d1150a1182e3d39c3cd16" +
+			"3920f1d7ed83992bc4116d9351ae1c6c4827d1374242e374310409f32d5f" +
+			"0f38c78b6489c568b791c70394d29ea2516dcb10e51bdad862ce3339d5e6" +
+			"14fe14f150961809c36e0a2c8eb872e9f7a1c0956fbc9194cb63ff9993e5" +
+			"d0dcf62c0f49e81dbe99f3656c4dea57b766ae9a11254f9970618f1b33c8" +
+			"f339f440de240170f7a21f03ff2da42102b323ce2b9b7d0de5aae324d1ba" +
+			"c87b1e4c5279a566bf659778f8b03882aded57377a0f1b063af2897060e4" +
+			"23be7cefd4aa9a28479c16773944d254fc21d3e1acdf508b7972372b5991" +
+			"3b8b088e93471a7d54c6ae4c52ba465ef07f19f269677fc2f64d3fb3d7f1" +
+			"9069d6c7001d4b002ed6683c59bd5651a450503b68a4a00820b8c17e3263" +
+			"18f32c21dfbcb2a02a104edaeff67ec09533aaf3d1a7fb41aa5d506ccdbb" +
+			"e6e35fa0a263c0aad3acc91182addf8c5bdfbd0626702694b8d652a63c65" +
+			"8d6b2b7c75d015630de508195e1fca9573b61bc549ca017c4bd888194d44" +
+			"3e031f36170215a301f922736a819f3ffda69117170d1933300366c5f2ae" +
+			"1052446ef7c3b82c5868be158a881597132f51c91c80c24ebf621393dc45" +
+			"05fe057364a76ae67494a8a5f67acb551cfe89f447df272ed9c1509fc330" +
+			"2c3e16541452d4d68438f26858724012ad3b72c094b9f166c6bedb8336a3" +
+			"41e032988f39cf53535789b320b5424d07b6bf5f8792e3aceb0e868765b8" +
+			"611d7905089949e0c273e2410c72a146cd63981f420405bd883e5390e985" +
+			"8214a8db714e8400a21d0636d7e5d9671a3582ab9ff032170b8dd6b9d5a2" +
+			"144d065228fa54aea9a22654df67f3f62c5fc59d68914d8b219829b536cd" +
+			"2ae937ecccdb6031d94cb3",
+	},
+	{
+		key: "84373472e362a356bd5c9b50f55c588d067b939009944f02564f136c62dac36b",
+		tag: "12dd5297cfcec53deae1dd5f9325d894",
+		in: "860d9b2954c3daf18fd67eb8bd9e6e3de2e4988ad9b04b1987219204dee2" +
+			"388db1c59a935de27bce29e7cd3ebdf038785efb35eabd4c3785a62b1d9c" +
+			"3ffa25e2273cfe5eb10b4ec6152cd8f21dea415421b452efc7cc4ea6bf1a" +
+			"b85fa6614e7f6d650125424865386ff8ab53247a63ff023b2d0753a9e5bd" +
+			"458d6ab0156fd3cf2d5002f902f927a847e8c4a8426b0a5191f5e237d590" +
+			"2659ce9be9024750d1d618a6b8dd57efb6c2bbac2930858f1132639391aa" +
+			"9e8a620a2a7d64bb7e943c77753401b5b619d95ef857df25a52b4eb97372" +
+			"a05416706b2644e2687bf1d42c0cf06e5eef8a1fc7e178440bfebb85c44a" +
+			"4837f69e43a1789728a999c5e04291576e757510f22bca11583a4e93688b" +
+			"442f2b2dab8d5ea9441ff09b8287862ca538ad979297cc75510a3d9ef36a" +
+			"662b4b7c373f184202befa5bf3f315642e6210763d033b7e2c59731cb356" +
+			"045e9470bf2f83cd62f11b3e904b0c0b1be99bcb805150ba7ef12b8df3ca" +
+			"bfc5055640687d710ab88e0fa8034b26112ebfd044a4b290b1c6f6d18c31" +
+			"ba9880b1cf2d81b5d02f00d6d351da5dbf47b6a5cb7b53eaf6de52c8a68d" +
+			"053602ccffa37ccb44a7683ab4f8a58c4bbc9e140e4e6f3cc10a5c07ebd6" +
+			"070818db983f9f415168606011efab6b8d7b4e61e8eadd8bfd8d028b89bf" +
+			"b0a16996252d7b4ee4f9ab50fc9d6e482ecf99beeabc38d70efbb9a0d4b7" +
+			"9a1c5d2835adf8e25111352eabd24d562644efc97637f695e4792f2049c6" +
+			"00f4d889ceb951cfe289adf159865d013046985d7fe2598014bf2dbbc528" +
+			"b4166fc2180e724ded8e7ea1c8d66338ec50d955d5594a0a7b4655338b70" +
+			"e8978485a722df814fdc6fd2436dbc060121fcb575672b2a5e454c1209bc" +
+			"2bb21a99d39dcb3c697306dbc2104d60fd8051c43ea2fce268987d0ec249" +
+			"a5c02f91d3b0dfee181b3cf8ef1ba9665daf7ea1f1d3b216e378943b78b6" +
+			"bb41e5dba095748bc776f8df6383033a1f5504955da3f42153b1c7ea83e2" +
+			"f90b990ea0c5bd3906b5c4060b19f447ec7762916b8766e5a23bc4d39cdf" +
+			"8e27752df8129b60ccee1731e47383b589d4fcad865eed4041a186df206e" +
+			"9fb69ab6ea092e36f186a6fea8d77bd7f3ab0fa0e29404d617317c75c832" +
+			"854427848237cfc18486c95f7213b9d53f324da036e8d298133b5003984a" +
+			"b9d71836f9f1b059db90005a9067c261bd85aaeed4d623df2220eb52b73d" +
+			"d683abcdee5cebd411996f853752f638bd28df6d78bec2ed3e00d7beea06" +
+			"2b81c19682ffb2f6abe3a3623a2e0570650c1384f1818d76fbefe3a7ef3f" +
+			"46138160ef897f9934e00e066e215230e719c23905dc60d7fa4d666fa52f" +
+			"e7737db15126d3262c3a4c385cdb23ff3b56c131e43b241f4a6062a1a248" +
+			"de9f13eb82c11f7b6a22c28904a1eb6513cdb11179067b13c7b5f83a58c1" +
+			"4f2753f19fdb356f124f52923249d6e4a2c8dadc8bb0fc91e360155a14c5" +
+			"c194334b9f0a566d51fad98592b59c1cc4b40eeddb34e64f337f83874884" +
+			"0583f853398c343dabc29b9444be1e316309fb8d81304d654b3d4bc4cff3" +
+			"55fc31278fe22e649324ef10acd247c0b72397edf96a1c16bbbef0640296" +
+			"4d219575fd23c36efc1fb8f8a34b510ba9bdfb3b478e236777ef7c6c47f5" +
+			"5a2bd0383d8eed3759456ffcffb15e61985b08c022658a5ffc875821bdf8" +
+			"83f69f096dcc72a96888c3af76db57a54be701759670bf05cc9015f5bf1a" +
+			"745cf755a25b1403a870875701427f820c4b29eccc260f30113629ba03e2" +
+			"785014bdcbf34d0c67aa6aca20d2dece811788686d5a45820d2980bf7d69" +
+			"d5c820a09bad7bd95166f63dcfbe8652565c285e60e2704955d69b3037d8" +
+			"7f5e6567d95b8891276d5cf7c59047d10a02ae4a28794405e2524ec2d595" +
+			"1b36ad1b9d5265fa098a033b88aa66cd9eaf01eea49c7dc4cc51c486f624" +
+			"507a2be23f152f43709b2cfecee44945ca506950e90e70164b77e12e1c13" +
+			"0b4d1021c2afa20038f190096276cd22e89b6e7dd10fd58fa033c9d42536" +
+			"98de3f4908203be8dbf259112f840c76726d982b4a837cae7139e27182b6" +
+			"1b4dfbcc50e42d5ab8532edfbd30f668879824e9ebc34b63ff1526cda81a" +
+			"e38352a774d79f73219500e57f0159a32326195d8895d965071834876a45" +
+			"c1a3c0bc4b1638535f7d40011cd5b23343fc27fa318c1aa3f9d8c43351c6" +
+			"6148dc2175e0e620813266da3000954dfa22048f305244629d512e852376" +
+			"6248a897a3ec3e2983aaa8a0f025f18feea57a5153a59b02604ebfcc7a9f" +
+			"b03e62443df88ead9dee955e23bcf6528c278a353f254c9484a67a7b263d" +
+			"a301923a4efb6866aeaaafd428e6da48781365bc49e90cd16b2388220d08" +
+			"bb9f79d14012b5a8299a651917b6a829488753b6ca449a14e8dd8c5fd5ef" +
+			"657d627b8e7773475b802655dc033694f24376e3b01e519d1aa8365d0e55" +
+			"92d0a4adbf555639b6d75d7ee59a7d12c6c11317b7927f11bbe75ed90508" +
+			"b0698420e231206704d22dd1f1740edbdcaf19a47d66ace4eecbcefb77b0" +
+			"85cfcfaced4d2d6048ce76434eb79990f0898adb4af2c377b581ebab3f3a" +
+			"150f40dcae002d4caa60050591c0de4ba83bfd59a08670beaa4641aa9829" +
+			"bdbb720d6eb8b2f3e864a98676a67271a82cffdca2b3590a0b5f97efa5d4" +
+			"ba062b4798707159782bedc75e5363d5f5d55ec2bef70db22955adf401fa" +
+			"c3b7af937816eb25d54d9f2a92e5a2a04bd8b8d7568204fd289f5ed2e033" +
+			"a76209d288e11e8a4dbb06b9029e90cb186446746853f02d738e06bba538" +
+			"894e03e2658ab3d7f9ac861d2cffdf12396004d1cd15f18812d3803ab9e0" +
+			"6f41c9b374d6a0678bb82ce06d9e3b9dbc8d2e90b8f64d0d040f3fa8a3fa" +
+			"8be71d2b3183cceae1bcbfa2353689d842f7d7052e5699dcc70ab2b58761" +
+			"7041e5aa1e2f41911d525505f061d3ca45152f5a7a1fab50c674e4597a52" +
+			"b46aafb4ba57413879cad1308321843abb7c39696fc2f2e225878bb1191e" +
+			"e151cc76f1a1b8d491c1672fecbf710db82dcd32554361967fc839c8e5d4" +
+			"e488856e1b9382eb3fc3bdc3b6886a3cd79761b02bafa080a745ef6afa26" +
+			"822f1d10d5e8eefb842837d82c9986e78fc3390caa142b7643de8f613e5a" +
+			"890a57f5883409549537f8139534f4ca1b60f33e42be25433f1d82add530" +
+			"6a4cfce258c0d4f1f3c9148ffb5c4b626d51f78ac20bff0393b7fdb4b9cd" +
+			"70fee7f69892c8a9ee089c6c5c7bee0a1b825e5b9517f2c82d6c149735fe" +
+			"45a8839812c2deb2a355b6230697053092eca450b7b0d3242b2689efe364" +
+			"09e820d91fa4932034d96495d9dd3baa4b385da815a7cb69438ff648b326" +
+			"e7efe8d688e88570ba59df7c439faf72c95317a10c984c5ec0043407e9fc" +
+			"9b46487810eac19d2bb40e0a654935f76e7d8861480c5f48419eb33084d4" +
+			"0e1070e5ad542c94f58b49e67dd05b6637a2c67d41451b7e00ba30eff221" +
+			"755d6d427ec634a2b95980d274a89579feccf1c7df3787a9435e588f2496" +
+			"06a93b7ac41c8aaa84b91c95cad9463d4881de7353d95b13bbde4c9da90b" +
+			"f1fe96257309a416407c64368b5564f022c4a493f2a39df1696f45801e42" +
+			"a5",
+	},
+	{
+		key: "2d0035a30d19b9cbc7a27561f3ab474c01115c4499b4adec660ea06ebaa1a14c",
+		tag: "a2c77b55cb0c076d8ea83cfe0e64f293",
+		in: "4e667580ba4f38f64e5cb5566bffb486dcae10cd17acb3754251e837767f" +
+			"16429bba2b832f29ba538f97f3556548d163be25e69f88fff0743150623b" +
+			"e0a1d82af9384ca335927a0e9cacc3dadbdf1e24fa5c81f2602d109e1400" +
+			"33929e409b9a0fa4f2653944edcb8b3ef963ba7f8806196c73bff0ded670" +
+			"c6def5d240c5f3daa121f8d5bec9b2a0b0f1d62d54b013dc742d6bd46325" +
+			"460f692b76d4991f0796820ddebf150c7d33829795784dd2759b334d2706" +
+			"70a7264941be5d99d460d078a9eedc3660cb3176ad302f9365f0bd698e46" +
+			"9f3e63511abc81109995dba17be1abe8bcd28407c7fc8d02c14794bb033e" +
+			"178a94f6dc73719d5bc235f980a16eccb4121ca83b13c4e165931ae4f192" +
+			"4292f8cfdf1c3ed40feb71e13d919b48fa296dddb4d23114a3d86ec10f16" +
+			"f314de4cef813ed24b49f4c7bc44cb8424df1f70e8d77366161c7cdd709e" +
+			"97610aca3a24fb2202ffe15eaaa25d711cb5179212a2c6497a13e5d7c365" +
+			"7bc502b3d2ebde2e57b714dd9bc21e73795f3d35d620613918c4c9aa0e89" +
+			"031481c97a5a4c15ec6abe42d40498c33d71c823bf1d5bb5fee457e2fff0" +
+			"bf777c80c6e3336ab3ce793440e74b336a8f7034f6ea2e4ff5ea4ea7c350" +
+			"65cf2ccd2da1d6df29bde10f4cc0202b5e4cf7ed097da49b970a6db41e5e" +
+			"98f3845b42f46663b1d1ff01da71389a8737ba8f51eac1ef357ba5ac9a80" +
+			"dd2c7f9476111dcd651fc33f4c86dc8658656f3f02a8878bc38ff0d0a1af" +
+			"2e31fb92eaef08c50195490818661feaf90e8b6f5daa1ebedb2cdbc8d5dc" +
+			"16db3505f9611ac46bc37931e02c1fd6aad6e4b7e187d5e6f990fddc9563" +
+			"2b33f55bf68b0db3890b11113ecc839a4fa4de25160e574289aabe4d8fb7" +
+			"9cecf9d2fa75ac8d0195beefbdfe0815f8d7d9751c1280a29b547149ec7c" +
+			"2295f5afa53cfb516158086bf203357eec2a5db71143f996c81555a47f92" +
+			"209719a71570a5553f1ff9b4b41827dd74657b463f36623565f0c9f4d2ee" +
+			"8735d6af56ceb3b3d0ec516b22f0ddafbc24647481f61ab169e2616c91c0" +
+			"e1f6a35436598ed801670e1dba76226cbd0544959ebe70f836c8a7df575c" +
+			"b907d780ed5aa0d6e4e8e0d2f457efe89a777374aa49d4961db96dbb787f" +
+			"021d99231001360d532a70ee1fb94bd6f26524dd4b7556c6d40e08723d7f" +
+			"9905aca66c4743f2bf8b34493bdabcfca617809a867bfe0a4f94c756a6a3" +
+			"dcd04ffc0a3ac671a0afefe0d5d447efcec48c6368998760db6a572676d4" +
+			"29b6d3d6e0c815650447748c4b27541c5447acfb8f7261b6378f3fc0fdd7" +
+			"375eb9d458648c7fe9cd96344f11aca912cc5098e9ee39e0b6794cc1dc2d" +
+			"f1b10f927102705efa20e667b63a91f935c17764650b287f5289d5790766" +
+			"555f31985c5aad94c652ba41fa9c0195d15405f1fcce9e23054a42c8a252" +
+			"da83bf6268782ba44edec5d8f94a20b1830cd1c5894cc6b9b52ad0b12a5e" +
+			"cf3195a32a0b02483ae3b954ac6f3af1e0f334221279d03a72138f3a2cb2" +
+			"1e706427c4d604674dab88d429f28a67be7a996126e077a1dcf8989d90d0" +
+			"8b08f4abb9a546b3c64ecaa287bf3468c59add86365b885f52afe13ed8d2" +
+			"69ea61832a7ecbb96ff3336f58a1eeaa6dde3611f3ff7c2cc8c9b745b0e8" +
+			"b5919914245a49ac192cd77d10deb9a249623f696065a532c20eef9e9b0f" +
+			"e706579566a9eeb14d4e8251a7750e29eaa60f034c1a7a1d51aa03a45fff" +
+			"89acf41080deec5506128b06f003fa46bc4021a82fad6a8052a49744ed69" +
+			"45bd9331b5ae80d873cd042bff079b2b9d8af8065a22c449c32a56dbbe7a" +
+			"80d0f3e30b9167532506915883dce0aa9cb749e4368c595c5bd33b57e36d" +
+			"98cc9bf91cbfa47331d69b5cbe9c92bc66c0fc9ca8717bfc108e1f710333" +
+			"14dba02a28b9aa05890cb01ae9175806c3c4215bd446f6cc96ec5d08982b" +
+			"4f83cd1646160e1d306b3cdec02d251f0901b03e8c3c35464eaa5082586b" +
+			"b55482db97599d513ed8d7a82e32fae302684b7ede058474c1fac7893444" +
+			"16fec93fb982accd162dd956ba2f31a894e9366eca00e6e997fbbf9a2980" +
+			"8b83a139f6432147a717381bb8baa2205715f735c1e0db273cdda6897c9f" +
+			"39bf0d7eb7caf93f657ef4d3fecea28baf69cf36d3cf347081df3114455e" +
+			"b4fe3e49ad3c3f14435e0b39b6c0d16db0fbcfd7ba8da8760d5952c03667" +
+			"251e7a4c3008cfb0904225e55c23b884bb09d26631650460c4240bd5a165" +
+			"b531ee76ba5749b3bc60adad35de519321c1672b47bc35fb59f7792a3495" +
+			"11b2bb3504ba4a28717823a27a1f99ce6970290b26efcf1e7a0399b10eb1" +
+			"0c1299c09b80f4520d00e7908d004d5b6a72a411759cfa9523f6b2912234" +
+			"481b1d8fe4c2365961c0528bd593d42bebb398b5836ae6ca013fe440adbb" +
+			"0090e8ea274f4d8bcae483e3663051a328f7c12870b40e4973a9797a2336" +
+			"3d3c53e1b0d1a9159bfb26158f44734b3c34b571be641bba2db937d4ae1e" +
+			"edc807b95b1c2a7d44804885536316ad38aedf0d83b1519661f2bb5283cb" +
+			"9c50dd61c3753433e988189f26962d1f4befd444257d0b6d5b819d5fd572" +
+			"22c9fdff032e07a4d8686d451e71de4748965309c0a2d7c422ab7cf3d96a" +
+			"8c0a1b0afb229debd1c9421cb828b9f2be96bb9d6b5be7ef8134bd9ccf81" +
+			"51620937d720d83dbdddbfaba8ecd2eab6f1974090efde0ca963e9fdd691" +
+			"ed0cc5e074c5780779222552fa46ddcd951763a32aa3a044ff4a73cbab41" +
+			"dabb3c2c03fcda68303477f0dc26f35bdb5c9bde721fba1a2db732a89629" +
+			"a8de3cfebc3918df1a9d5053d09da5b7316e3285bf62156ca28cb64d343e" +
+			"72445fd66757bf4ab374fe7932a65f3d7fb6e42cb12e5b67ddf8530383a4" +
+			"6c1ee7ec8883e454a467df1aa7e468a6e7035515f473901efca5d46ff358" +
+			"70e0cc2575bbd7f8866c8e73cb157903a1694ff3051424f28de826984dcd" +
+			"065dc3658df144ae3a6d37b88c367e3cf7c58169dfdedda4a2821ce22188" +
+			"40472ff72f0dd1a6b0100555ff188b80f835259a634405e3dad61fc299f9" +
+			"307e27503b2cb7714bf3b636cc64b61d2e374119c8ef8adb21f1516c7fe2" +
+			"38c807818065bf312003c12e02525d69d9629a99e4ac66ad2e792f302cd2" +
+			"a6f5f702dd28040738a084a7052f2c3ed0924c33b7a5d357b7c9a29cebd8" +
+			"621a4bfb7bb34676ff210d59f7f9d4eafb7c5c490c9ea48402af5bb072c4" +
+			"731bdebcbed4e8e08a67931b6d7342d4ef7bc4a75ca1dfbd32ed6027d8fc" +
+			"b71e3f55565c02e06daa8c579b69774889181291c470576a99e11f2c5acf" +
+			"77e091ef65ed243d4287176f7f6ac7aba6908c9ff1fa43b894a499b642ad" +
+			"c01b2fa1c4b58801411941bb448f1f7a04794d2cfe5db1be61f7b86d6eca" +
+			"c547ee51d4c9050f9e9f318dae958c150acc21c878f0c7df6065294eb1d9" +
+			"a278c920838a0db752b080a32e67ac312fa76b589a385f31847196076ed8" +
+			"1021fcc375bfcc8e1361878e2693860eb21ff0595e4eaaf7897f2b79367f" +
+			"7c4f711279bf0c93a97dcb1cd8d87e444ad5f4cb5c1de44e37868c6743f1" +
+			"cd72cec376726f26c8bd4836f9a9f9c68042f95ca6f9d7cde493e531c553" +
+			"8bf7ace6dd768db69ac7b41ce93e8ca27ff20a83ff2148ec5b89e05d8b8f" +
+			"5d78d0fe16b96f6eb8d3b20126a186085c6825df81aa16b3dbf57eabc360" +
+			"71299ccdda60e250c652408d9cd1da94d73c728440ae08fddb901aec0fac" +
+			"1050a778b10f94f84883bee158bc53b1c001807c43a3151fbf581b18dda2" +
+			"527430872834e5c380575c54b7aa50f817cf3249fb943d46933cad32092e" +
+			"bfc575bd31cc744b7405580a5f2eabe27a02eec31e0d7306750adbbb9f08" +
+			"c78cb2d4c738b2274c7310cbf8dd0e59138b6a91b8253ae9512fe3d7367e" +
+			"a965ac44d54a7ed664e5e5c3c6c2d942eac388cd32beffb38f",
+	},
+	{
+		key: "2f29d71d73f7af98f96b34e939e1a21e2789ec6271b878bbebd14d7942d30080",
+		tag: "ec02f4953a9a63ab6f2bfc3501e4fab8",
+		in: "0e0950987f3508239063e26a13727fefcdfd2cea6a903615c64bf12d9ed3" +
+			"887f9b2cf7ccaa196ccc7756b09471475b9daefd4261e69abd23b9faf9c5" +
+			"1fd5d5788bb39d3c068fa6807d30f6201d3f6dfd31715d08b1733440cde1" +
+			"049608d23c4e45c5ed61f863350232f85827e7c292dc5f1eced1cbc912e3" +
+			"f5c420bd945911d3881ede5153d3b2cc85371fff98d2caf97cad6ef59001" +
+			"4017f9690cab08989851c2647e77e81401714a93ed9f938b79f8f54e3133" +
+			"fc2cdef259df2ba0d48f37bf9e43792e3a777214cf4aab6dde6deeb543a8" +
+			"813b71b5974136c1220d6218a252881f0f5677ff5b6aba127f19a5f3c5aa" +
+			"c988543d7839a90a3f947c4e4d5c6ae1ab48dbd40456d1aa65339a4c15eb" +
+			"520e8ff9f965ac4c37735937cf09942e7958f8a6cddee41707423f715903" +
+			"ffe0d15af8c3140d3a736d23be7485fceb9f07c6509f2c506eda4ec9d30c" +
+			"cc133708f48d8828e332808c84a745d337296d871b9794de1c5d06534aaf" +
+			"65587526a84e2521f8b332645e0e72564bb308ecf99b7bc69608474389d1" +
+			"686ffab8c49b7f04dadc28d2ecdd0f508dad2135843304e378b3bc7a4f25" +
+			"7fa4316be956e0a021edb8045f39fa9f002087f067199bd6001acaadd261" +
+			"4bf6aefd3f098f92a959685f24bb2206c347359d9c6adc6847117bb434ac" +
+			"6c40ec618f6ae8b75a5e2e4d44c332b7b06c8b4d521493b9b0bde8894209" +
+			"717a24b320214297b62dec741cea018ea681c9b56702068528b3726953e8" +
+			"c5e4ccd5029e4183e772d9834a56a88d45bf87603dfda40e03f7e894766a" +
+			"7623ab4dcc0dfc3086d17566945069173935916f772e2a5f8e1547348f28" +
+			"782400fc069ac0e2b94242e9e0f1ba2d0e76898f9b986540e61ea64d7f69" +
+			"1006b86ce61565da75eb16a8b4c5865ca4eebdde2190e354734bda94fe7e" +
+			"12ff47dcb5d5e6ad93cfadcc491cb350b09ffe391a157e14b65e3a211b5d" +
+			"4e447c3ff95571dbab33a83126d68dfddf9383b4359d4103ca64af1e6963" +
+			"d09e17eb944aa71e76711dca33168586bfc44ebe9fdc55497d83f238c66d" +
+			"bcb16063bc85635f0f1a6280563bca49ef971db96a41b6ac5e0642643262" +
+			"61eb4662f3d6ad4cac826db895de22c9b8aa35e6464a7f44e1ae7238e355" +
+			"068d68754ffcca76c50b7ce7ef9bfebac9eeab32c87d059cc7ef2adb5d57" +
+			"c7419adb394eef48441952253e8391e555730e29789d6293c3696f441449" +
+			"0aebe2bbe541e191a6652ffbec1192f0f9395b7ea370aefc1f1cc8438035" +
+			"d7681f12f1e11d6e334da188b10c302fc0f4bcf1de448090510a8f1d5683" +
+			"0c943a3c388b33a038c26741a4cf3487313f755fe7a28e25e44b5383c5f4" +
+			"cd6ef34d7dd73462226281899dc3f2e69809a0150f694673f31addc89888" +
+			"072a7d4ecd63d6b90540f9522ec05829a7f17d48728345ad808fb0203883" +
+			"3cbd018d612992a88df944b8e34a70920b3f26cda2e8bb16c3aa38b12b33" +
+			"b395c9ba5e809f60ff05f087112151af1b5987403cff8bb2dce79093f431" +
+			"2c744f911a6f3091e4f9ef9375c4dce4c241d2f6024a1797321851ca316c" +
+			"4e460fc060e7839deaff8ab5e8bf682c0f21ab6952eb793cffe690db911f" +
+			"50b11f56ea352942c43bfff51d4360882754faeb7cf28b6b32bf7fc9ca71" +
+			"fbfe1d72be05b8bac9ba513d731e2c9d13d6f2f10eb926edaaf0e3996656" +
+			"da8718a8e103c59326529e91ebac6ed52657c9690ccbf81028cd9fb189ec" +
+			"4de94fc0771e53302c8d9082835a68780cccd772660a110a1b40c57bef3a" +
+			"c1d69428aea549ed17663a96895a66a3bb5ff6ff61dc64908df49b760caf" +
+			"a5aff05e2766a418dbaa1e7d189a9edd55a04fee8c9d6e506d299abc36a9" +
+			"d67be035fea5d220f41d081af67615fe627c4dd04bd8659c7fa4f57f35d0" +
+			"db40d9684aa178d7483ed5d86f04eaea412e0ea05a4698377dbff4fc3a39" +
+			"1f6ce0cb833d3118d6c69319b511cce65fdc74928e270da0c537f8201eff" +
+			"77416155d4a39c7ad38c22cdbf7d2b7ff7d85383c178a835ec604c3f9ee3" +
+			"7399f7dd826e34f1a35ab75da44ba56f86097ddc0f3658ef5bd65a24f4de" +
+			"4255d0b03411a9d7f0ddc29e33cb865da23393471aa94e6c9e72e789206d" +
+			"3ba118aecd39727068f528f01b25fae2280d70033e4ee46b41b864bb922e" +
+			"001d8bf46d6fbaa5a594e926f45eb3a4d2f074506d7834b606f43c89699a" +
+			"6db00b374658d9333700894d440a712a1f25f5538f9e7c8ee57ae7e612df" +
+			"13292c8ba9dbede4fb77cc6c8944aaef59ea6ad3b36db398f4bb0f82d40b" +
+			"44879835f224d6e05992b1b8a68dd58c3dbda2fd73786492ee48c7a25f87" +
+			"264b766930fe9427487504fad17f8d230934f044e49ba219f26ead728856" +
+			"cb30eecc33a3946d3b1b781061f2458c7c46f6d96f3e06f369f97be91835" +
+			"f23b38347d1e381ad5be4419275772c2abd549522a0203c1ee9c96faefe1" +
+			"df413c4b7b2624417890e0716854b7092b3b3b368cb674035d3e6bab2357" +
+			"e7c262b606f7141b6dad2f6145ebc1deb7597814719784f3c17848a90ffb" +
+			"cb0289e2f3cc7da12442b837c4e47f468bca3eb4e944a31c48562c2f144e" +
+			"9e920ab5e4cf90a14ccadbae29af13db38cda911e3c8f6f525e6722809b5" +
+			"31a4de1926ab12f643d25af87eb8610df59eded6ec278242247dc69a4213" +
+			"13f7c2b26ae7a917c1bdaf66c56876e9104d40b59e6ca1431ddb77fc89f3" +
+			"14b46a154cf127688564a4f9e120d7b5816cd24a6e095dc8ab8b43bc3639" +
+			"329719f0e0f723e2f5136d82638e2249e648ebca67cf0306741e9e8d45cb" +
+			"903bca85485c4007397c88a1ce07266f4f611b96b7e0ace3074247a7dfb1" +
+			"cdbbdd66e25e172fd2bda74abde7f3b4cb5cc7ee7859f053b2f04f9de03b" +
+			"a8e96264117f502087c3ddbee8d850bf3618b4de90f7b3e562dfa57e4426" +
+			"5357236e35e71d1669226d63bca50b1b944ac07a1f794e73e80985689b25" +
+			"f18fc709367d63b8639d71865cee667536040be827145c08cf3e57a66678" +
+			"4c81115706a146eccadc7aa1a9f074b47e95bcba7db8108a13279077bef2" +
+			"64699fb87e5abf5b05ff3879d7c7c5169c7cae817c13f0859d4e9c05db0f" +
+			"74c045ecc30a51e515feea627da387ff780719395b5b9ad93179b16fad10" +
+			"5856049169dcebd43a7f39c549762405f807378e854b1654a1179d895ef0" +
+			"85aafc72c7fe1e0e1cd3abf8e20935e331145bbcece4f17ad24ebb6c64ea" +
+			"73bd98a7494c134859206c9422f7c4a057db0ae0770c4bcb08c1a6b9ca4b" +
+			"7dd8c1cdb3e4977c7ce6c1e79b9d6ad98e27d2759b53cee73ec037a8b686" +
+			"f1ff78eb8421f41c74ce9c62a90d38b75159ec925f232e0db71362f31e29" +
+			"4336f5580a34b26c5a01ee3454cba227c7f400f6889a319d7121dcea27b9" +
+			"584f33ac796d48a9a24cc5b6799ee12f10725fbc10d7cf83e4b87d9c444b" +
+			"f43e2f5ee49d8f3b531ebb58fed4234cb8bcab1b8b18bf50956506baae8b" +
+			"c1b7492250f3adf64294310387f1d4bcac12652895d4f2dce26f380733ce" +
+			"0b5820e9fcd8512a1585a49940a32fc8875ac3c9542a4270602e5e97e720" +
+			"90ed71b51badb775340429fdbe45b887fb9ee61cf9e091c06092cf0a2129" +
+			"b26572574c46910cb458bca7c63eddd29d89753d57e568323e380065794d" +
+			"3fa1ffb874543f5b0ddc702b087e91e22604d9600d37fa0dd90d7acb2458" +
+			"4cd408a4e66bb781dde5f39efda6a8fc26be0d08ffdf851e422ab1500c28" +
+			"bf6b4c85bdfa94e8aef5cda22870c39ad49c3c6acdbb3b0d58cd05424c65" +
+			"20740b5c2bce4336545eda12716317df58e6fb764fcb3004f5248c5ccd84" +
+			"f63abdc0dd2a64e447c0de4da4a1082a729d8ebe14810d396933085cde18" +
+			"318278481fdb9a748b637cacb491f5234bfe16b53a35da6677336baeedb7" +
+			"4a28c19a412e7812dace251446d40ec07afd63854c3dffbd5c0f6a9a3cac" +
+			"ee3bab07fba94800fd1fa0fe44f5f2ecb2b4a188cd02b8a2df0728347c50" +
+			"7d0cc58fcd5d54dffdbda11dd1bcc59758396ed8db77498fbe13238d3d8a" +
+			"0040194dfe66811542ddaa658094a9580d4e4b4e29",
+	},
+	{
+		key: "1285f117bd90b70ef078ae62f37d2218419e894b7d334759ddb2d88833b287b5",
+		tag: "429b2b39195a10357043c9601590a277",
+		in: "00ef065a1adb4ce7108b497813ccc748933fa8442689a7cb8dc7c1ffdbf6" +
+			"c09adfe05ca2cc5ec3acb7493f3497ee8f9cd9bb8a4b332c18e33f78114a" +
+			"c8f9a72ddb9f13494e934ad711818909831013ba195b53f5e9e5b4689399" +
+			"6d0b669f3860958a32b85a21009d47fddbc8697b7c9b92dc75d5060eb4fb" +
+			"40aed7a1dbe69dbbeb6296f5467ea2426cd17d323671fa408855bc53e5c2" +
+			"d111203ae38cecac7719c0bd7f21f6bd6a1588187b3b513983627b80ac0b" +
+			"300b7fa038af1cc8512403ac2cea6e406595202ec3e74014d94cf8780ed0" +
+			"33c570e887ca7fb35ee4768202aa52427d02c24e63f7f2cede95ca9909e9" +
+			"dfa86246a27db757750667c198c9aff4ce348f7ac51864b36ef5695df713" +
+			"d17b8f561a972d0136bd9ee9aa16079c2ab5d29ac9ab472255ade05dc49c" +
+			"b966e0c1c04258ef9ec59ded01f402d9fdcd9a2020a2038a8c78892ca218" +
+			"30136069485527069132959dab2b81c73ca590fde2a7ecff761d95a54d63" +
+			"a2664aa5a6deec163e46b5225bc98976a4f363063b0f42e29f792d138af8" +
+			"eae68d3854b5c1985d5cd1c9f49f529b0b4d2c936887b5b92cdebacef992" +
+			"c35e0b7bbd52114aff8c6b261852e28e451b02099814f809b0289cba0586" +
+			"04a363e3f969aad3d982f645ec4c549f943fb360fb8fa0d5a597bf89842f" +
+			"8ced6014a5b2590ef71524a7ad50fe0ef0e2f81b6e26b99f9ebbc8036549" +
+			"f7eacbf6ab884710c6406ff59788e03ede35c30d4781ad5af171e0623e8f" +
+			"cf5344d71165f0475e256e9159040f702b359a2963116ed135dd6c1d111d" +
+			"2a1e33e15c178ca4f02c5fb15593c50cf9a8a492f01e04778dbb81d26c99" +
+			"0c58cf50a9bcf4fe38fbfc0fc0685d8bd422a773c7bce649f7a86c59118e" +
+			"f5f857b2c72508cd1ef05e1a0c0b7ab4687fdd57437092eb49bf41a9ae8b" +
+			"bd98272ea2f8ee2515ff267fa6ae892c266a7effe61ed54984924aefc461" +
+			"6cf483dec024ad666bc797beaa429a742d1b8806f67d451b6d3a85b4d474" +
+			"003cfe9e9dd906df47da5559c41f15afabecc3e6af279cca0f2a200eb2e8" +
+			"31437e034d457fc880f60f5ae635690bce82bf6d1ad6b4f5344ec042bf25" +
+			"7d010273c861e3ac516e9ee2bab3a255f570baa32298467bf704bf6d9076" +
+			"a4c0b08a528a05cd1fcbdf51f3885fbaba7891a144fc058919903b269b4a" +
+			"29f43926eda32c38853b814a7d528156c223748d674d8f7f5448350f011b" +
+			"bfab1511001b8014e20fee37ccd4a0456f638c197c86dc116b34f955c0b7" +
+			"dee10bac5ea0c2fec8a780ac05098b51b902ca6afff4db3c6fb4f761df79" +
+			"b2039dc5f16d9402442a6fcf6c4297769e6c36824d908beba8e584ea0b3a" +
+			"91b9017baeefac651d0307bd89f517789236c0693c65a5a20f244d39684c" +
+			"eb810cd2ffd3c78fe9285d2eb9f55d133b86113efb8dffcbc6d258e84c38" +
+			"2dd8f4d7d63b65672516d9bfcc3310a79ce244b60d380128d529487f99b7" +
+			"d532d5f5c28fad8b9a071fd2fab8fd98f6d7ed9dadbd2fc4396476eba6e2" +
+			"1a1b1cc594a31fbd3418d98e4aa736cab285a2786fbbd4650e49f9b080ed" +
+			"3fda34941c28d25545395e1408fc3e60730d0696061f821a4d24123cadf2" +
+			"3af3d37ba7ce1ba3cde1368d468f136df82c02f9be9210022192aa02117a" +
+			"ef5ff70bcfeffd47bc37b920826a4d3db001f956939abc0df520f3ec1613" +
+			"ba1c4b3385cad97e42bfd15a3150711fe86ba4562f17780cee1cdf198615" +
+			"ca06270db84986f33e1d53d552b0da82397c496a23c7a78ca7641a908e71" +
+			"89249cc657c0431f1e09ae0213f28a27e6267e9d17b5bba0ea4f3c21f266" +
+			"fe538e215ec62f85517ae6bd87799ac5ce68453f09cbbc50d6e2a168f0cf" +
+			"7166ad50cb65b6c76406c326573c00e04a3186251c6181933828c58f4198" +
+			"f8208c4484805639b0d428fd05b57e4356239638f458a84000c7a7a8de62" +
+			"ec25b54d1e39d2579ec9c512fec475f243576f35efc02a1cd6b0478e2dc8" +
+			"be5f17aa4e3849cd42e76fbffe6e7d6f912d6edf80f718f94a7e48e1fc10" +
+			"6cac29627d9d4b82f05a30cd7c739f7f3ef7ea368d22612f189da450e274" +
+			"de7b61c6361521e684d639be5af4cb11fefa5fce6f8a5065c90873e504c1" +
+			"2c940571ea7bd7e9221129b83039d2edb069e8b5bb68567d8fcae34c6ee0" +
+			"cb94474d8b056cc3c7403873f2fe6db3b567a44e702e4f4813b2a264231b" +
+			"0a998207b41916715ef94e5eec281589d0a711f8e74be32bc60f43d693de" +
+			"77f21d5f7eef892abe87725f3d2b01d9ddb6dee15f40735a8fb67766dbcd" +
+			"020a93b8eef4361dc3a891d521551f65dbe6e3f68c60819b0a540b0991c6" +
+			"4449d207cf5b1c198c17ad6caf3adc628d09fa0baae7a696d84e1879577c" +
+			"ffe9b3f62669d4ea5ebab6364f08c66d170ee4a94d61fb77d60b33dd6b60" +
+			"650f034c5c9879243d5c16f853dd7a89885a9047a341b076912d47872b3b" +
+			"3de49edf7451b435698ac4e182d16c339be83e18531a34aebad36c5c7c93" +
+			"aaf121cf99ff92d3844d40740fe001eeca9ee71300d826bc3cfc87a29d39" +
+			"ea108a3cf259657ec4b967fbb534e7513ef3a96bffb35abc5ce0e890696e" +
+			"54fab515af3d2c0be6e003747504e486c0ec6e30fa4ca79d6596ae0425f3" +
+			"396e40fd37432e52c74f812250dad603b3502f97ada48a26e39fd4d44584" +
+			"6591bfa5ffb3770d95d3dbd49e9c3a38c6305796b8f7d79bd0845170925d" +
+			"575774445299bdf9d3f8ad3dc2dc5cfd3ef0293b84d6e11370851af05ebf" +
+			"b3510a22edd930797dcb76b759a9b5a77ed8dd5130e79ff5ac44b01901bb" +
+			"79603cecf674202bc5d84076ff41b3c806454ce80cb9e5fa9db77294d20e" +
+			"6d3008ae3017aba712862ecd4b32daafef1b8cc8b19ee8f8bc3835e2372b" +
+			"5cec66222ad5ea9df753c033508ec43c8b5995e88c36c13ea3465c8bc462" +
+			"ae0a659d9767db34499e9d01fb1588410257d6f588b3fdb766a66bce28b5" +
+			"e0880f8cf988a2e5eb5bf80cd7d83192b7392fbb2e3a07d51aea2b6bfac0" +
+			"d74d304f56d5af3598a0712cb09c04c5dc14194eca8e1b9b29f88344c0ea" +
+			"55638c0f8ebb70b6242b797fe2525fa1bde76293dbc0a66ab4715e6f9b11" +
+			"f7ecd8f35a20ee4ff3552caf01bb307e257ec0576023d624d6094d43d25a" +
+			"aadfce939a6808f8baacb2109c3de50a1cfada9e384cdba3e97d2c9025a3" +
+			"2377bb195fce68c5569d2d1267e1bc68fcd925ddb4acf567fb29ea80517a" +
+			"7e4056fb014cdee597333ac2408157ff60cfa1afdc363a11fd4883308cab" +
+			"d9a8fe56c2b41c95eaef854f20bf5941ed23156d86de3bd413465a3bc74d" +
+			"5acffcd15722879849c261c1bbe987f89a1f00b3069453841b7da667d566" +
+			"e41fd894d94de44c23fed08d9bdffb723aa8449bf236261240d865efd7b1" +
+			"74a4460e5004ff77f4196d1d421227dff7c78f1726df7b5eebddb4bb5f57" +
+			"5ade25296dda2e71ab87ea2b44ef2ce8742a7ad5c1e7a40e097eb336561e" +
+			"865515f7ee0efbe01d5a928f208f7c9f2f58974d1c11af0e737c673dc446" +
+			"1795da9757010cefc6e7f2784658717938735ed8cbcbd7981a1bb8f31cab" +
+			"b901c87a3218dd1195c59f64d0bc3ce8b72580fe38e6dbf1181e0090e5c6" +
+			"d162df9f31cc52fa6a8ac61897e9b4b3cb0ca2bfb38a38d9b78e46d775d5" +
+			"7645d2d6da16bda8edd8675e2ba121f7f85400cf7cacb9ffcdfae583fb93" +
+			"753d07985a00afc3a4e26c9939a5116d9b61196502f5d774ab4c7fb6cfa6" +
+			"01bcfddcfabfcd28055e858d7d3c19feb6bd7c02565add3a3af61bfba8b6" +
+			"f4b52c072a8613e878368318383143059a98a85ba521f781a8983c2486ba" +
+			"b83f5b91fce02acee0be8d0dda7489975f0506c8f363b5adc48ba971adeb" +
+			"4e1c830b5f264ed42da36d2b5ce2fdab1e63333b1061ec5a44ec1b6e99da" +
+			"0f25e7f7250e788fe3f1b8e64467d3d709aeb7360720f854afe38e190cc0" +
+			"925c6cbd77fbfccc07d8beeb0ce68e47442fadaf13b53c30a03ce317cf79" +
+			"dc9155ddf96814583695f15c970fd0b6cea0b04b1825eb26e65ea9351bf2" +
+			"f7a841ddaa8c9f8e885b7c30b9985bac23d3ce777b",
+	},
+	{
+		key: "491ebd0dddefc9f0117176772f9bab61b92a1f1de13796176091c56d1e53dfbe",
+		tag: "fbd3f884a3dc2a8be06ce03883282e1e",
+		in: "953b9a40789b206fb507ec2c5e9c88ca1baf25ad24c11a62f664db1da8bf" +
+			"dbe9b54f8e93b0bfb4adb12f8873096b8960fd91eb92a8ddb53232ac9141" +
+			"57caced33424cff943a8db129049af7e7b733afbec6637d8ee4f39d063e2" +
+			"be241cca6a339e48d72372efabceac57220692c40856532d95529adfae87" +
+			"a71c72f30244126d01a875375ad8836ef8db929bc81027935042a05c346f" +
+			"bc94dcc057db015e55c56064d2b11154596b813ee64b73bcac05d2688bf6" +
+			"f1fbb0cf3f8307b3df44c3e2dd1d226a4d0e9dc5f7482bada9611970f887" +
+			"f656dcb19ce1f8c5c86f4cbd1e4f49b18f170ecfd184028e769e79d7424f" +
+			"d01cb315897c21111f53f4d41c3b71402eea695272cb5b4e5f33abb9df50" +
+			"cbdaa55ed629d3ed7d93b43e550295502db1f2ed884afc320518e88be4c6" +
+			"b62a13f8d3636ba091d07dbc6c20c7e7fda016c05b2fadcfc9ea32f4ee2c" +
+			"4893de78ad8a1771aacf6efdbd8fb1f6ee9b0572ced3edc6313185b5d398" +
+			"88ce77950aa4c5201a256e3ae3e74f05b70faada14124b35b105a70e7769" +
+			"7184576b69708eaabd36e0ba885fc6bafd5738a67307a1181792333cddfd" +
+			"a4ef19c88497c82fccff05a8f9f732fc7505f0467a14e135288ee018aef3" +
+			"d0412f6b0760573d8ee4ab455d2789b4d22a42eebdf60616fe403627cfca" +
+			"fea672bd0a49e8e7b80e7b7b8feebce3381f2fc16819a8996a99ea230c3a" +
+			"84b510cf2e0d914610d646a2f45a14268ec1d6fca03d0aea5c9ae1c8d519" +
+			"b0e8b0f6fb8ad176b5d6aa620b253cc492b5e5645353fbd9b6c02bea48f0" +
+			"286e2c669782b5ffefa4d8f3f1037151026d9cca78e7808dfbe61df29e82" +
+			"951d7154f3c97606cd1e99300012578ea6a776dcef0811338b56606b51a6" +
+			"9893fe68f762af6c9c26066b1d503e64877d8cd988b443af66a36af8bdfa" +
+			"41b4dfb3721d1d81895884755b9c52527030afdfaecd66d4638fab1d1786" +
+			"3d5517ef7ee7d081b5555d24991810f1edde30930fd392f817cfe632b4ca" +
+			"6fb0460c36bde4a5620b9c369bf51c7d870c43998b8171a553d2f643fe8a" +
+			"58aabfce8cf7363ea978ff4d53f58284db822ca95b80306ec02a64d26a29" +
+			"c98520f1924c70d161682c54d08a2c48f54bb72980a8cf5babd0aaf0fd72" +
+			"7d5b1b9d9b731dc49bad228fe83f7347750e277a4fbd526983c206e075d6" +
+			"a03d68957b3e925a71bc1ea7304c77660d112a5d19fd21a785d4a8d7f2eb" +
+			"dc4183376d8125341eb28b2df5be0b4e04bbf95c47d2fe2aed939619cb97" +
+			"79548b752f57b723cf8295dfce69c9b7486b75a4e900f91926636f3fc78f" +
+			"7b7720a5151abdf5868fecf1e1a1d830cd6a4c5e3cd739da4432cf1fe2af" +
+			"a1090d6a1eeb32e7236ecfddb9d07b97220ab8e23edcc93d91abc11b0c30" +
+			"460d2027869d1c2487070cf60b85ad0b8bc5df566f6fdb0e58fd044da530" +
+			"6d277e564ca6cbfa820ca73fb6201b240a5a94c4ecd11d466cdc44046a66" +
+			"32478221bfa69b3a2cebd16baa302a573c90895d7f4cab453b11e3a4d8bb" +
+			"b5a9bf264781ce5b9796e3c47d0fa57f46b923889af4d073270a360dae8d" +
+			"51d85ea916f14787c6500d2d906ccaaa92d20d93edd09139f79bfeb5fcd9" +
+			"8c1cdbcbe9f2587e9c9094e3c4a32ab9ba56f400b929e80c0551f953896b" +
+			"e8eda6ecf22e6d4a541957dec21d6a9cf388ff0ba58169ab934902892a58" +
+			"86e1126b16118e965a271495ffa339c49466209ed3875b568a4290b7b949" +
+			"69d0465744a3c2a75c599c3a04ab1a3fd09125fe8f45724b2f48c7822b9f" +
+			"ef95af4b758ae66a8b6646df7a0a1aabe2a24c052fd6d30561cae0389263" +
+			"e3388c4c1effe431a04356c334aac64f36593544885c4b7295b57dc39638" +
+			"b665b22dcbf7dd6da867615de38c6a575cc66391135d47f8e1f0c73c6129" +
+			"17ada4099723933a758d83311b384364263cad5fe14bdd7c825d9601c400" +
+			"3537a5aca7f9da4710c132ce8b0f1464cee625633ef57f507739a0ab1cd2" +
+			"21ae634d4d0b3ff07e9ecb1baaef0a82a97279d46543a0464855cd62c07d" +
+			"5e890265612906a9eac88bec07b1dea5f67054c31ae40f8c673296cc5df7" +
+			"f0dd8cc9e643b44fd90dc2d1e870ad8acdbe165237642fd04c00965837cf" +
+			"bd2344ae830887a5719a3c16dc8ec08bd9131d055bfb959b64ff4cb638a1" +
+			"002a4fe02e369871cc4e3ffda17dd85343e679fab43e11970e60198b424b" +
+			"676ab17fb0dee10cc9c2e92b32b68d5b05b7a559176f822850c0557ed98b" +
+			"7454916e32af549a0027db95f02b88cfc5e7e05f28f53757dd97cc0f0594" +
+			"212f8801e58043cb17b040413c226dfce2104a172d218caa4353890de17d" +
+			"be1f53af6ceda24b8781801516cc51de9ca459e469b3c322be13d8c9541f" +
+			"755c518ca41a0ed42e44b9f87faa2a968b0292216e9f3d3e8987282103e5" +
+			"016fe9f7681496e1e8d663eb2d8bc30b41d735465527f19e336a98d2dc54" +
+			"d7c020bfab30fe6c62cbae7d09f84af69bc2c51a1839ffba15015d381ba0" +
+			"a44a3758771c4f18d13827f518f30bb74f4bff29a87d4b9e949f1063f63f" +
+			"662721cfd64ffe1dab3761852387f78fa83fb48ae2c75fc567475b673da6" +
+			"fa8f53770b6e5a3c9fad951ec099c6bc1e72d1c489e1ae620e7f12ddc29f" +
+			"ed65f29c65cef75014b999d739e2e6e015f928a30f2fee3f2e59bf65b54d" +
+			"89948bf2bfde98b076e5460643952befd02fc1b0f472a8b75195c53ea296" +
+			"6403b9028db529cd04b97231bac3068855fa211f4d976a88bc27a0088f04" +
+			"576e2487ac0467992066ef7667ca8429faee92db38003728e5c219c751f6" +
+			"6f011b5d679fdd957f4575a0cfb6b54693a9624f2c7e66c578f5f0367005" +
+			"c66addd1e3ab7ea1ac404e357cbdab9438b9b4f80b3a6761b864b006f1df" +
+			"689ae4c0434b06b686d5353d3e421b57381ea24fdcf6199195ccdb3d5cf4" +
+			"623a6bb1f9eba9b22fa15395f65f8093b5f90455061c1cbf8128b44a31e3" +
+			"910862a59e187aa7f4d22e0317ae6c177cef24eebc44171f70c25efac73b" +
+			"38ada0cba0b74f72d1c171277a734819c1111ebe46d5db20a6ff20e2c1a9" +
+			"a57edae95a3c1f80ddf2b12c86d3df0078a7bf68695b16ccf92053c727a4" +
+			"80586b8d87d0d1772e456fde0c20a7927f351a641bff5f22f9ee2217b6a2" +
+			"d0983c8102d7d5356dea60a19e105ce366b9d000987c8c33396569f97c56" +
+			"2d0fc0bc5859779aa10efd1f8df0909c307a9110083cc6d9748456c9bddf" +
+			"16dccee52b7974867cec718bb0b76b3353379a621257094277a30148ac38" +
+			"e5cf67ed7cc9c1bae12dbdeb99d7d880ce98e17f0dc93c5330d1824a3c9e" +
+			"ffd86f89e15b59a4bee5a48d4f674766896e187abaa39917b83f8d2f3265" +
+			"bbe7aac44c9f8d92f775fe6493e85ab44e6e28f79f28eff156c21e1abdae" +
+			"d10a291b88c4020b1ae8be001080870847a852d073e82bfc751028ac62d5" +
+			"6aeac1b18f2cff1c0c7d336bf08f8cd5099d9d3b28f9e16077e9caabab49" +
+			"f2d234616a7522a6bde1a3b3c608df4cc74a6c633d4c8068138abda8d26b" +
+			"4ca70f95d152888fb32bdee5dfad8ff4a5b002a0a327c873656db8d6fdd8" +
+			"ed882e47ce8e47c729e1292db9122ce2e9fa275f9bb986eb7e0a1dccb7cf" +
+			"abd0449c92fd35e2aedc4aa89caf53bcd28170cae85e93f93988e723a896" +
+			"10cefb4edb6fa545835fba3107e21dceb272c5a32da26fa77df070f41d7c" +
+			"ad1d68b836199ff0f1221e36b9b976b5e69bed54b5bfec67fe9cbb383484" +
+			"696265204797634594bc335150daea92dbc1004f613b4c27bf5c699debf9" +
+			"4365041b5a894701da68a93bcb61f4e546c553fe61f14ab0322b45915da6" +
+			"ecacaa093b0071f2516ca8c3fef2f1e3c403993d734403c47bfe5f4379e9" +
+			"cb5b613fde3c0d880cecef4101aad8b8b1c60a92ac5185f6c243fdf1711b" +
+			"0b56f0fd8e5ed6cc0f99da888e4f156455a0f0eb365b8964347eedd15d80" +
+			"2f297977af667ed1376dfcc610f5152421b97afaaf16f9db57a435328595" +
+			"b9aa00b5ed9ff106c66970fafef379f4d2f98f2c5984ea05aad64651fbf7" +
+			"7968c8cbc4e959859b85302a88a3c2faed37765f3f6ced59d8feb6c72e71" +
+			"f9d4497d98bccf95fcb650f29131e1df1bf06a5443f8af844aa1a7b5a68e" +
+			"bb250c7de3a65ae9b1086cf83f832050e55030d0f67c6a54ea2a1dbe18e2" +
+			"8a96c9e0dea2966997bfc5c5afd4244e3c8477c4f5e8bee8fc8ca9a5cde4" +
+			"d9c5a2c7f3d2e811b1de7ce4279229319e432674c609b4c8b70dc6172e9e" +
+			"653fe1969bbc2cb3685e64fd81d96d33",
+	},
+	{
+		key: "b41db44465a0f0d70093f0303bbd7776017bca8461c92116595ae89f1da1e95f",
+		tag: "d8a111a09db22b841fa28367ce35438b",
+		in: "b074b0984fb83749586881e8ec2c5ce9e086cfb2aad17b42b2429d4cf43a" +
+			"0400fd15352d182e6c51e9338da892f886f460d40bd178d81c52e9ab9c1c" +
+			"bdd812594e6fe7a9bb7fb729c11328d3288604097600a0c151fa3d9e4268" +
+			"de75866558e9f47d8dd331994bf69f826fd4a6cb475ae5e18365f59a477a" +
+			"dde7fbcf7e40b4e3dee020a115830b86f0faae561751e9b596c07491c42d" +
+			"e02fc979e69071113953729d7b99f1867116d058a90f1b8c0f9ba12c6322" +
+			"4ebd1b563a87734f5d6e2d4e6715d5f0213e33316500cc4b23784f78a9bf" +
+			"13fdf99bfe149cf47aeaaeb9df1cee140c3c1264fe89bcde8acda6bde16c" +
+			"e3d770ba51950b67ad2c5232ae0cff048ddfda8540cf18e673582dc96987" +
+			"4b127f655e7d4e08859f2c6b95403cd5b4e2c21f72bb872e49e592306286" +
+			"48ba1b16fc9637709636b198f9a297aec364d4c3bc869dcad32b1830e434" +
+			"b556b429136f0012a0a0b6fb3797bc8668014b010ea51674ef8865348dcc" +
+			"197672047fcf72e6b6910a0e32a4f110d85e28db0e338d9cfdec715a8800" +
+			"b4f007a7951d09e41620815848c89f8768344c50bd522c46f64ac6c98e53" +
+			"92176651961c7a70b62f3d1819bfda674e2ecd3167415edc4b97419e8ae4" +
+			"9974b56cd8d52e1d05b82610b59606a750b34844ca33bfc9b21fb970738d" +
+			"b66f48928df79cf67730a30b0b612f8c15c22892120548ab460a6b9bb3ac" +
+			"e30554c86c9681c797821a1b1ce91d0e87fe90ad4097c974cfbdfd5c4c24" +
+			"a5f808f388e1b1473e858f48a387614501c8c39d6973ded69b1764663cd5" +
+			"166be02b596a49e392d637e3d8afc91323f7450318b79d5488c040e346cf" +
+			"0cee512044514b570aa66bb98d639a9ee23a7cebe28474592623d082873b" +
+			"73efb3eaa4721fc4761e15a390497cb13cce181107e8b1a0186b9e47a5a4" +
+			"b67a5be3cd88a43d341ef63f10af6970aaf56035db938655020809033a92" +
+			"8d4fe6d2f5424fbde2fe82adfd991d388edf293cb4e3eb68d876f225a5f1" +
+			"58208bcb1aaefcbc28d6763d267406aa8d6ecb413d18cff7a318ba031ba6" +
+			"0ac4560748c248de64eec56dd4540124b38581604f502d94a2004f9eb1d6" +
+			"edb009e16af6c6d3ccbea79b10743da98aee7ace407a90c6cfdde694f36b" +
+			"e0271e722618a457be68619b980754795f4ac95ebf4f1820b85ca8e3fbff" +
+			"a2430f8e01ab422d7140751f7741f2c921400dac404b04e049736738a87b" +
+			"6f49bd54b1b447b922c473831a65f224ab84fc96e4551a0333bc6187e15c" +
+			"c0f0ad628068bcd7c043bd1e3036ec01e7fdc3d157476149917baafaced0" +
+			"15d09fafb92181a0ec65b00c9c13631e65de184377416e04d3d93b847e0e" +
+			"286c1d88245d4d550d30d4fbfcb416ff26a39a94275631c2deafc7cb6780" +
+			"f149e4d0e9c4515b708fcd62be5252485407a6ceeb9247de34e0266ef384" +
+			"976f6d31284c97468b3b03e951d87a5a00836ea303a266147a79ff3431b4" +
+			"b382e86c74d92661e0f65e266b7d569c03994b667a8137f3080eda2ff542" +
+			"0f0b52b427558dc26932a22a615c9e6b1834a251c6b68fdfc0bbe0e8781e" +
+			"36adf669f2d78bd23509ef7e086634e526258e8d11a1e0be0a678ac09c7b" +
+			"b4e3c5758504011e701dc85997fe2a3e40c7af83f032bdbe7adc10ef1e4a" +
+			"666946c2bf31dd8e3a383211c9684d5302f89dafcf77976d5a02c14e2462" +
+			"09d2d99918e82402cb0eacaa12032ad8316315af1b3d3bd5058f7c935d35" +
+			"ef0d4e71373958fd5e4140a9a586d89c53e4144c00148a4706a524896eb0" +
+			"5b1479a0de5d3f57be46b3f5fa4e49bffe027c81a33e37abc01a4cafe08b" +
+			"8e21fa86b42be52d75d6407e6cdf399de7aedb9b61a6917b2677b211c979" +
+			"33536664c637a57ce2234e3319fe8b4a77d7285ae6347464dfd0aab3e6f1" +
+			"178e0029686770d3b0dd541490b097f001e95f27efe8eb16e4747937d643" +
+			"cdefd49e586ecad541270cedc3064bdb7c79f086bf1fa8c666304d977a15" +
+			"54ae268881e17d8bc3fe51fa9969f7e560e3d3e050424febec0998b35f2a" +
+			"7378b2c3e384cbfc80c4987734d76c78224cb81cc5376f88f0ceda28aa50" +
+			"44e956537c3ee209071d84a66173384e0aa466d989759fb1f2f17fe627a0" +
+			"ffeaae7c5a3884b237f5151278a07117c2e833f1815c7e0e0b1611f25058" +
+			"ca338d21deb1a571faf1d0486667cb7c58e2814c3722d24fb77ce1b7e018" +
+			"2ae5746442b5ad00208b17c0a68bab4df8a8f36edead4fbe79b4c9220dd6" +
+			"acea6d23c7caaf6ce7cabeeca677a1c764d610ea6c7e994d6a9c88f57fda" +
+			"ef160b251e7595578ea2cc1441d480c14b8b6945e76a001891b1f214979b" +
+			"c52ec15e9480d706a40cb6e3b259ee99a9e84e63a738f1b52cf71c8ecb04" +
+			"fc833c2c680bfed587aa1541e5ffe8bbd7b21302bbf745011e559f94f952" +
+			"8b7fad8a37f6d855306a5be22725859cc950bcc334179d49564af3b9c78c" +
+			"e1de59a9cb45086a33856ba7195c17cef573950155bea73ed16645768bf0" +
+			"a5cefce78ba3ff98a54a8e8afc5dfcb0d422bd811ba9b7770a663b081dbb" +
+			"40aefffbeabca955a9638830f0c5d70663cbf5b26067cd061c4a3f5cf8fa" +
+			"4b6678d82d9a2aa33f8538b7499a3466f6b0ae2a1daf280ab91a6c220684" +
+			"12705245f353b4b83db50bedd3bf99d42bde6363fd6212cb745467acb007" +
+			"b678128f6580629a06171f7f3af272f8900b801af3bf47439167871e7b0c" +
+			"33f198333992a6c52c32be46071738cfbf245937d48f816ebb88ff0e726a" +
+			"dc41de4c771ff0bd320a4c0b1fcccd9fd6c42ec9c5185943c70e9a4b7c26" +
+			"a980afe104bb1f99576671a254704c7d4233eaf9915e1d56c103ba9f6e8a" +
+			"46aff466933bf58c9842796ae9cd21f7ac6aa96ef42ca54e390203bac354" +
+			"b7c1de7d1887c48255201335f819020e2782a2ee8af92ceb206b651ae92b" +
+			"3f4fdefed05e08974aee0a353d104b1be9a5e75c7f958f1981271b0a6928" +
+			"05a7a2f28a0448d86102b4fadf9ab4ec2f98e31e64fcfdf2b524780b3342" +
+			"7a2a3100c2032fc93199f3ea7a9e8063fe73282dcb1fafaa9496c7da868f" +
+			"dcf33bbb761df0bfc6fef30fadd2b6efef4fd3216a8aee48a2ef28102491" +
+			"cf7278b567c272d1064a277eb193b3f6f01df641ddb729f72454943cbd3b" +
+			"671ec077f9e3548f5f57d063c653ebee4f228a78f8a128d26f7f4b44160a" +
+			"07e942bab87b2d043c77ecdf10c1a419e0a1c4162a99c21d4abae0558b8f" +
+			"4dc0b7f1ca3892a6babf71f2f70aaca26bb813ac884ee5d71abd273ff1c4" +
+			"add230a771b678afbb12a1ca7fbcb2c0f5589c9ce67fe8f78a8db87825b3" +
+			"09ca34f48ac35aa7ac69c2fb2423807650fcf47ee5529e9d79dd2628718e" +
+			"230ffe5b83f9d5bdfd9c5d211282e71cbcacf972995bf1b13d21419f7fa2" +
+			"8829ed1dcc459da35883b9269a474f7fceff01d44ab78caf1ef7d8117f50" +
+			"cc83eb624062b149a6ed06ddd1cd1feafccdee7122353e7b3eb82978ca69" +
+			"247fde52d2d6cfe7324f04af5259e1b5c2460889da4541b431ba342a1c25" +
+			"3a1b1b65fce7120829e5466e7ad2fe4e0f773c7c13954a9c92d906c91aa1" +
+			"de211f40916596bfa8245344e257e5907a2c49ebcc864cfbe28663e700d8" +
+			"472c50355313d5cf088e9e8a19cdd85bcfc483520498c6386050e53a3ff8" +
+			"1e2b77b55b116a853d71f60d621265166cd7e95ff5cb4466226d7cef68ff" +
+			"d0a35b61e76a43cdcfa8da7fff9558e2f89b981ec6be632b126303ca1fe8" +
+			"53d5c628d967d39317b60ac904d6a882beb0746f6925a86693aff4deaac2" +
+			"e5b64b611de86767d55a6e11221605508b1c5cc828251539b1b6f65c2c04" +
+			"8e65be5422c1b11194eb687d906c559068c0a810713b23b30d8b17f10df7" +
+			"0962c5e7e782aff7bb95adfe4cba9d90b0ebc975fa56822025100b5cb8b3" +
+			"8bdc8928c1a2a8034dd66e2a763696d7ce6cef4dd586b83f7d01749d37fc" +
+			"4fe8d7abd324d4ff1efdbdbfeb0a2fbb8b266fc2bce8e5e5b95d0089e7c5" +
+			"d7de4db837d1822ac8db8198889d6bfe778d0b19e842f12b5afd740aaecd" +
+			"e36e2cefc2cf0b082aa0c4f75684d024b8d828d8f2911fe1aae270251f62" +
+			"4f49584e40bb193577c9d8e04eb16c094653cdf9a15fe9210f724c7a7c73" +
+			"74cfd1a74abb5ceae88ea54f7e7569f8eb674529cbec965ed05bb62f1968" +
+			"8fdaa97297268bfeefd06eb21f700cc56f9bf7f6cecbbbe7278ada8399fb" +
+			"960371a2d5cdb852b11c9fa17650e614c5297bf46cb7889d52bcf49d2560" +
+			"720852822b75bb16524d88273cb366b84b88282da91875562e5a1fe73973" +
+			"afe90e5cdd3f5381612d3ba7bfa058d023a9326e403ec474d8938313fb32" +
+			"bdb5bf899b900c3818c43c8a0af6a061bd26e847ed75983402ee8a9cf4ef" +
+			"85bba5545a0d329ba81495157eda0286f1917de512fe448251697dea406d" +
+			"a510adcb05",
+	},
+	{
+		key: "b78d5b3019688e6ef5980c17d28d7f543ca5b8f9f360f805ee459717ca0d85a1",
+		tag: "f01babc4901e957d0c2032a7279321e1",
+		in: "ba7d35b2ef8af1118bce1e78018c9314b0c8c320591e103d23f715acb05e" +
+			"dc98fbc618de06627661df5842dbba9f604c2d20d664e5db06e949b11d49" +
+			"665088dbafdb0d39d20beaca7d723f8dcdc57e9c5583d303b6cdfdbecf95" +
+			"7d8daf2f1c72b2a6fa27e3d18841f4841abafd334c110cd2b74efb6191db" +
+			"ab9b8fc8427ee17664082f31db98d30bf15dda967e20730a9ef525abe9f3" +
+			"f620e559ed22bf74d347c9869f0311f33da7f1a3dc858b3a8aa73a35989d" +
+			"b055a4a2c269c95e352259c57de8b94d8de48984ecde426d3ef60ec1c7b4" +
+			"41cc950f7764f55bd0cf52d069b9ad446d1f765f35d02ec104ffcc00bf1e" +
+			"dc1b951ef953acd19984ff1b41041bea0e9f5326a7c9ed97e6aab42174ee" +
+			"971ea1dbe2fd1c1f67f977ab215962b0195417170f6b7748fd57262424d6" +
+			"cf7c235b34425f4047191232722932213b3eb73904cadd6a2e9c7571d7c6" +
+			"6c2f705b5039ff75e5e71c5aa738bf4177653e6eb0b49303a4bc0e641e91" +
+			"2691f217296a3325431d578d615afddf47784e4618a2ca40ccecb05d621d" +
+			"a52f272b8cf84f7fd8177c83af1580d25a764cc06436d67171cb5d1e3b39" +
+			"367b46d9a59d849d87ab6bfcf3fb9bac2b1ebfcd1cef4459e74b0e1b7080" +
+			"dabd2dea79f75581a55de63c4b23ff67d986ad060102933fc6cce8d614c9" +
+			"c86dc84068828dd9e21ffc5665c809d83b09432fd315dfce5d7a4ebd8143" +
+			"181953e3f8716e47b0b30cc1f753e31a7d509f2dbd4177b6da310cf3cd02" +
+			"5db270adf98e96259a5ae1b81f5be4d5c76f502a612ca73c76b91e0ca695" +
+			"aa921f9489948619482c2956205ae71fffc3aba4476ff754e4878e36c763" +
+			"2c935c076857c5b90cd63ea4764efbcee53e2ddc9bdce54b1cbbcf0e7544" +
+			"d023e7c2b79419ad92221a1f76abe31a8236e370d38e2493cc9ca2aaa811" +
+			"30fc713d11f500fd071d6eba6861e8b0859b372e62fe60b627a96c377f66" +
+			"236aedf307e1d148a61bdad072b93d7d2a73367c595b1e048f7023e72729" +
+			"1ec508326f5424a5bbf4e010d0240b71fa9137e6642ab40c5e4fff79877d" +
+			"b3253c663a221b49b3e77ea307c7b9f3f72a0f3a54d0112c45c64a0c0034" +
+			"baf2b55ae36ea6f811bbb480cee663136474dacac174c73b1e8be817916c" +
+			"fd4eb1876582bb3a36cfbabad91776aa676305ddf568a86e3a5eb687fa81" +
+			"67771fca7b5ca00e974b3cc3e322b4bd9bcee2a87d0ae7976da5e04fa18c" +
+			"219fa988d4f6fce62f194b05c26ed3ae1b066cd9751a2d916d53426a454d" +
+			"58f9c3b2fb49374e5791b412fdee1b6029144f1ca787f56fece4f64f4fac" +
+			"bfe4cfd8ba7c807a83cf44008fe5126a283ab2631a87acd8e2a3bd10979c" +
+			"4b07a84a49b0687a45a4798ded0b5e9b2acce30e714d78395bfa8f33ca91" +
+			"e68b2138bd67d8a694cd87c88dcefcd101a3b408d7a9095cc6a4b38898ec" +
+			"c8b375f5a67deaaf73eb7e99b10314ca6bba824658bee85dd731d9a1475f" +
+			"976b7c0aed4b67b088f0db5ca5091273217f724969dff6cf184181377c45" +
+			"5722beb23fd9d097a82ea2d8d527ba6284acc20cb30f2e52af28800c61fd" +
+			"1faf9f4f619550e0162a1a63758e202533889b27420fe7d0eac9a47a6e11" +
+			"1d80054412340e0426cdddbb3c7b9b823b8db3ef58230fad7a3ac21a7805" +
+			"d30878d4ea78dda95c951b7a5dc552e9434c35e03e1dd88652d3714f8fbe" +
+			"a39936cc0717c2e0335371f2a751204f5d9386baaec853f019325edfd1b0" +
+			"719d1fdac3fbd774a64bf957fc54039501f66df94b5b9b82c2076c597065" +
+			"dfcfe58b2e215a3734066aeb685ef97759c704b5f32dd672ba59b74806cf" +
+			"ad5daeeb98d16f7332ff0ca713d541c84e4aef0750bab7477ea707e2e497" +
+			"e12882dbc0765106070ec6a722d08fe5c84a677817b28fa3a41a6117f2f5" +
+			"465c2a2f0eb2b8be4f36e676b4115008bade3573c86cfb1370c03b6b0dc4" +
+			"bbbb0ada4dedac10a593655068a26febc2bf10d869cac84e046c9c846ce7" +
+			"927431f606f07b92abdfd81260199ae05ed01dfa07088c56a6a8de9c6d51" +
+			"d61d6a6d3f9904c216ea8329467a006a3d2495a768a39ef99a21827d2def" +
+			"909bb743fed7209f7fe59ff1c1e710095b05f166c6173deef5c6ec4105c5" +
+			"fc3b87c8269c786bebd999af4acbf12d20453b125f338aee87e9509ee405" +
+			"9c9e568e336304d7be9ffe81d1700555b0800242d9b7450d7256f2b17f6e" +
+			"d46a39f67bb2980572ce73169e352070dbafd4c7fa5a6be78cf9b72981c0" +
+			"a01f1e1e30ee3736c59828b791d2373799854497a28a44bbe0e074925723" +
+			"4986696fbb06ef9ea83fbd49c45a583ce12ff10258ba06127c67b0f66dd1" +
+			"09f1366d8036853973d8884f93de54fb2a12949eefc020717eff47898cef" +
+			"306b5de068411f1e113ffdfe2556e0faedc3e27d95a45b8afc15ba0eeeff" +
+			"eb86da7b4324e20af80c62bf0ceb4aee1515f5912f71c6bf2febf20123e3" +
+			"dd3a82dc1e58a108f1039942dcdacdeb1f0ad0b2ef34488d98d6a52311ae" +
+			"acbd03c12f6e775e375d5979c7c295bb049f2cfd3580e3da3841ddd8e6af" +
+			"4de5e6512ca79cebcab9280554524881da37984d340e8f0163fe10a02ed0" +
+			"88682560bc6d3c4dbcf1a542ffb3dcc2ed16a2eb96896e8269697ffeb50b" +
+			"73f2cc354092e782a0072fc12e1eaff117c2cc8a5a1ad8b47802ac9e23fb" +
+			"91a0cef9e4027595e0885464e61563093ee2b1dc5f22dfd04af7de6a70d5" +
+			"977d3751a4b3cc0c71a71c59c0534cb1f8c0eeddcf1c0e1b3e5ad0d083b6" +
+			"6e8b998ddf9ae9d3b365c851d42e995b9afdf8d66b2ac40bf514ce32e456" +
+			"0880afd38c42c08926067eb243c4b1184e667ba756c14ace5f525eb48df7" +
+			"ebb429d0a23d159664f8021d27dc7167081de331c7114c9c6456e1ffdb42" +
+			"2172a81c06d8deca995e158c48df27261a83f83e0127f5e056a139be9b76" +
+			"e25dadf534d3d1ed6ebc0b5d77d51e5b90ff86f30d4023066115bc11b33c" +
+			"c827b1103098826d0bf8777176b2da6f1e5b580e407ccf7e614fdf4f5b53" +
+			"3ef6d30b20c1bee61eab90e983b1a97173a62720ffd27abb8976a948d532" +
+			"d06596c23b0ef31c79831bead8f8e99ad209af3658cac0cb3c3f9c88379b" +
+			"9bc871d8e84171d53400902da1243f664afeaff60bd96ba2639a7644676c" +
+			"a79f43130af12ba2c877d67f7ec030a4217a72f5368af7c9f24e643db6ac" +
+			"97a04adaf57dbc53762d8dfa1afd49667c4041adcb5ec303e191b786273b" +
+			"bb065cd9f16a3a4a399c6a7aab9c1a6604998264e8b3dbd13d8f2228b13b" +
+			"2c2b9fec5055d8e9f2df1d9a25e4bfe2029776389877bbef7e2c7621f06b" +
+			"c0b7fc0786e2b2d042483ccd4a59d2872a6c5ac73e217123e5c8401580a8" +
+			"d967e0895aaa28f4d25ce68c90b4394d8113bc423e9fae46ac47bc2ac191" +
+			"fb97b80b5a85feb2bb54f84c493235c1408662fe253c6786fcf6fdb8be87" +
+			"dc66a72cc847f94dfb5214af5905b7039a7363a1b23a07853daa26862783" +
+			"ba08a80846fbb93ce98700a4f9961115128dd67bd7d19e0c588fdf6196c1" +
+			"1cb0154002ae862f11421f5dc3a57b6c0870b452272be556a1d14eab1af0" +
+			"a91ff5b89de6bbeed6e03bc64f5efddf9e54da71c594bc5ef78e0192cfde" +
+			"da36e4ad1a6b0b51110c1b24d20dea1f19e18cb1184d80189f842d4f07ac" +
+			"834744dd009aa3771b1e5502fe4b65a403a4bb319e1880ff6ba852e90a8f" +
+			"4fcb52cf374c88408428cdb1255291b04ed58c992310955198d61fa1fd9d" +
+			"762d48f2f65a287773efc67d549981c291b427889d3e3dfc0cc6cd68415c" +
+			"dbed81b516786dacf431472a7dfc99688d15bb6c1b85b1a2015a106e5de8" +
+			"cb9eec4c80b17d00fdcf4a9c64de4643a95dade8fa9f1bc5c839037d86c1" +
+			"3800a244188e3b18561a74912ed72f99f2365f0126732d037dd54a3ab77f" +
+			"9a9f6a1c1469ea92eb707482066bd4990dec4d7614ccb4ea6dd4deb8bee2" +
+			"2c4dc0b9b4d4cc70a500d2c8a5ac3ef88a38439b7dc254a6d920cfd317a8" +
+			"4d7747148c65b6730709e43369d4c995b03c58b9df444f77f216944e70f6" +
+			"6446554d8d513b8f7f28ef0a2d7ad5ca2f6110304196953247a7ac184f68" +
+			"61fba896c2d5a59007ec2b2c8e263957e54cdc1f3b4a145228823fdf0960" +
+			"c33a28f59b03ee4be21001d2f56fd49ed14db33b2c4eec2c3f41b250a624" +
+			"99a9b6602c1e838526a54cdcd058af1c252d56009d4c7769deace53bdb66" +
+			"543f5a081cdde775e61efa70956fe2a7a6019a164c6e413ded314bc928b4" +
+			"aebccb946ffdf3eb33e187bf421febe26112b3262a526de65678cd1fa03b" +
+			"83513705108fe0bb87aa99aceb28af3641c46a2c4427cc1063de01aedaea" +
+			"fba68155d4de494a27ff6b7fcc8f5c5c3f7d3a115c397a1a295bc55aec8f" +
+			"7f150cbce2a8aa4706d54ec863877bb966ad441c57e612a1b5d438b98d9e" +
+			"fcdfe6d4f66e885f96407e038015cf974ae5a3540692b054d2ddfde59b28" +
+			"ede7e2f581eeb56c5b88e2779aea60c1d8ca6107b0cdda1ac93e6c7520da" +
+			"edc66afeed12f980e20e1e1c327d15ade4bb90de30b011a9cb33855ca3ca" +
+			"e2",
+	},
+	{
+		key: "2b0b0fd3347e73c2fa3a9234e2787e690a11aec97a1c6d555ff7b4047b36f372",
+		tag: "81b1a6633f849ab0aa7baafa58a5d9b8",
+		in: "427f3a7a5f1142ffa68e83df5f917e07b2bc454f3adce068a8ae9e0908e1" +
+			"3e0099aaa9074697593c6d8c2528fedddeca05e3888be1a0a201c389a72d" +
+			"20cb661017544d95a431e70e7c6580d8fb46ea4495bc59db6ae2cd69510a" +
+			"02426c50de1b6110120f759960605aca718d4d0a497e003e1ea2b8ae9a53" +
+			"df3c1eb4f704eb32f8f05eb08cecba0fd4a94f0daa3b0984c30a38f94b7a" +
+			"10cde723182d30588bc40f1f9d38a3bab4800fdd5148e34e396144763696" +
+			"c9b3e9b8adfdb337123d54237c7413f98bb2056152b256e37a27bb947c67" +
+			"240fa3ce8da62ab367db540bcdd9eb873d6c71c75a08fe99b5c11ec8e6af" +
+			"f926d2adfcf073479de394d4aac5fdc6241824d944b8773db604c59afc01" +
+			"495ee755905e5616f256c8a64321d743a1c9368d46418826d99b762e2f6b" +
+			"f998d37a995969cdc1de85f0ce3987c6550459f5e5bfd9173bfcb9e0112a" +
+			"d91f092de446beba14fb3b8ce3fb2f9c941815b2cb5a3b406e2d887b7912" +
+			"bba07c8dc7caab9836827da93ca71fa5ada810da1e5e9b09738524564d8c" +
+			"923746d19c78dc9107b9f20f653e05d7f2eb6bd90cf5eb30fdd7b587eb46" +
+			"74a1064c70ef0af2e75373044d32b78d96eb1db3112342d38dca0e47b96e" +
+			"9307fcdd711b1c66355186369a28481cb47ef6bf6651c2ff7ee4665247cb" +
+			"12b573933d3b626d1c6264c88bd77873c2e73e73ee649216bf0b6d6615ab" +
+			"245c43569d0b8096596f25ceca8667661de1cd60dd575697370ebd63f7e9" +
+			"5333e8a2cdb829b75ea83d72cd246d50358f7c094c8a515805fda03165d5" +
+			"21391617c9f9a2ea562b419632df611a67912d2b369e5e505dbd5c719253" +
+			"16d66cd608cc4a9583a8eaa4661b7279870345fac3031631c1a220551527" +
+			"5be7d8d89b71960e687aace3a0e8f206e475053d6fbf97717b154c75406f" +
+			"2caa97d1ab66048f1c99281c188a2f37b8bfc736c25840a9130ef2031c05" +
+			"6acd9dc10592eddf94f5bac85319b10ae46cc136a0738aa803837287ed7e" +
+			"dafe08d1fcf31d5e63763e39a5e1f4d7d0edab368d44e63fdb33c28905ff" +
+			"d6be406a024c017081b4f2d70860776e9d2556cd008fa5017b58733da13c" +
+			"634938407a118827a80baa28d4e605db59430f65862b90cd8356baa287b8" +
+			"4e6d9199fd80abb9fa697e2c2c4c760128e4ec0438388cf407e2a2fe0f57" +
+			"908187ed8efd4c5cb83cc91dbe6a11444eede85099149ca82921bc28bdd6" +
+			"b9999594a41d97307f8854b1bf77b697e8cdd4daead2aa49fbc571aa44c0" +
+			"bc84a57cb5fd85f06847ad897ceaf449eec45bddd4e4eb1e1e119d15d5e7" +
+			"90957e686acbdda1bbe47ea935ebc4b8c2e3cf9b7157cc6dc03bcb19508d" +
+			"a9e19cb76d166da55559ec7e0995d9b50c6c45932d5b46eee400c56d9dee" +
+			"618977dcf6f76e3e86bc5207493afbc2aae9f569ec9277f33d9f61c03d59" +
+			"dd6d8250ee8cb3e54e5e941afb74f0735c41d52ef967610c9f55b2b52868" +
+			"4b549a99ae3392a7237bb52ff5f8d97327e2837268e767bed0bea51f76bf" +
+			"88bf0286bf22b881f93f1d54fab5cd4e3c148c96c39e7aeef375de249df0" +
+			"4d89d1bd97a7afb2be0cbfd3380cb861d31e4ad1ea8627721e4518b9db3c" +
+			"cda20273ec23549c4adc3c027e3ac9558de2010a0263c1225a77dac8be60" +
+			"d498b913f91391d8b2656ffddb06e748cb454dc2b7226745f11030a6b9ae" +
+			"09ac8ac428d9c6500801fb540650c94610ab70465b1210c6db2064dc84dd" +
+			"7f52573f8f40c281470e85176c85ec6de3c718663d30ad6b3dfc1a3a9606" +
+			"1936744357ca62fb8bb066aa1fcac6d7a2adf0a635cd546bef39fbd3ee0a" +
+			"8802ab0466ec9b049b5892a9befa4377cd199a887c34569b6f90852139a7" +
+			"86babc0049ee2b527aa96b988237a52eae8b4b49d2ee15ee5294118cee62" +
+			"3c3e11cecb836b21af88555f10be2eff8379beb615b7b3d6c01d545cacf6" +
+			"61be8ebbf7a3c58ac5e0e7b17997659a2bf15f2b2e3d680d142fd29d23a7" +
+			"aea9890f3ff7c337fce49ecedaf38573edfae07810ba9806723e576d687e" +
+			"a11700b8ccb96a6559259c367cef4e3999a05a373ab00a5672ce8b3d1dec" +
+			"a414187f383e449d10021b73c1f7e39ce01516b7af96193f9993036049fc" +
+			"72ac059ef36b2bcfbe13acf140d41592880fb8294ebffb98eb428ce9e65e" +
+			"1094521bcf8ecd71b84c7064539a7a1aac1ad2a8a22558fb3febe8a44b87" +
+			"72fc00c735773d4ce2868a0b478ee574b4f2e2ceb189221d36780b66212c" +
+			"dd8fd3627cf2faaa23a3d0b3cd7779b4d2b7f5b01eb8f1d78f5b6549c32a" +
+			"cc27945b5209f2dc82979324aebb5a80ab8a3b02129d358a7a98003e701c" +
+			"788a64de89726da470010eda8fdcf3da58b020fadc8970fafb08a29bef20" +
+			"2bd0707e994015258b08958fc2af4c86c3a570443fe6e1d786d7617b0c66" +
+			"29a6d9a97740c487622b5b8186c529d7f8af04d9f0a9f883043f08103ca4" +
+			"d70057ee76639f3b1046d86928d54cd79fb5bb7b46defdf15d2f8578568f" +
+			"1d7b73e475e798ec6812586700e038ed4791b23ac9439d679a1a4bc04cea" +
+			"e328330c24b065c9cdcdcedfbaf58e5299779e6f48783d29ec3b1643bc8f" +
+			"1095c724dea75770583b15797fc666f787510d91e65a8e2090cc1ed2013f" +
+			"e63ab17bc7640ee817487f4eac8326e9c4698cb4df05d01bae8c0d00fc00" +
+			"08919484d5e386c8f60b8ac097c93c025d74faa56e8cb688d1f0c554fc95" +
+			"aae30873e09aae39b2b53b1fd330b8546e82d9e09bbb80132d794c46263f" +
+			"4fd7b45fda61f86576dec52c49f2373e4dca31f276d033e155bbcdda82af" +
+			"8f823948498f4949bf23a08f4c8ca5fcc8598b89c7691a13e5aba3299ee0" +
+			"0b479b031463a11b97a9d0ed3189d60a6b6c2390fa5c27ce27e28384e4fb" +
+			"04291b476f01689292ace4db14abcb22a1a37556675c3497ac08098dfd94" +
+			"d682401cabec239377dff592c91aca7eb86634e9d5a2848161dc9f8c0c3a" +
+			"f7b6a728371fac9be057107b32634478476a34cbc8b95f83e5b7c08d28f6" +
+			"fb793e557513ca4c5342b124ad7808c7de9ecd2ac22d35d6d3c9ce2f8418" +
+			"7f16103879ed1f4827d1537f7a92b5bbd7cd12d1ecc13b91b2257ad073b7" +
+			"a9b1ea8f56b781bea1bddf19b3d7b5973f1065fb72105bb4aeecca5b7513" +
+			"ffd44d62bf41751e58490f171eb9e9eb6d57ffebedd4f77dd32f4016b769" +
+			"fed08dd96929e8efb39774d3c694b0d30c58610541dcfab3c1cd34970195" +
+			"7bf50204acd498da7e83947815e40f42338204392563a7b9039c8583a4dc" +
+			"faba5eaf2d0c27ada3b357b4fccd1595b9de09c607ebf20c537eb5b214b8" +
+			"e358cd97992fa5487bc1572c8459c583116a71e87c45c0ba2ca801931a47" +
+			"a18ef0785ebbe420790a30278d2d0d42a0225d211900618438d1a0b2d5be" +
+			"d14f8b4be850dc8cb08d775a011683a69ee1970bb114d8d5017de492f672" +
+			"09062d9ba3616e256d24078536f30489e4dacd6429ed37aab9b73c53fdd8" +
+			"a8a7aff1b914b9d82d75a46d0ccf85f48d3ce9a8d3f959b596ae9994ac3e" +
+			"3b4af137d0c8e07ece1b21fd8aa05522ba98f85a7ab24ed8c1e265fadf4e" +
+			"9a18c5ab5684d8ba8d3382ad53b415c73ebfaba35abeebaf973b6f18e0d8" +
+			"7f019420eb34e09bbb12afc5b149f1e9e9b6ae36ebde429d437ada1a2d52" +
+			"b998f7c75ef731132aafc3bb106a2ad3ae11223a355804d4869ebaa47166" +
+			"2df261d95d48ac6eb17c1781e81c0027ccf8f05c39e1eda7793cb16622be" +
+			"ce7a1ad5d2f72f8bf4bdb2f4f4dcadac3db3bf727f0d447adddad4500360" +
+			"09ee011bf4155e5e46c74b00d72e8e6a88de9a81a5a4685651b90e874dfe" +
+			"eba41698c98370fd9e99619ce59ebb8342417d03fc724f9c910ae36ac5e5" +
+			"b46c424141073199aaac34232a8e17ebbfdd80eb75e82290de92968f3893" +
+			"0ab53dc83ac433833576e86fbabfb9d7cd792c7e062811f4cb017710f841" +
+			"1e0fb65ea4b3cd68b0af132cb08330aa13579196ec632091476f268b44ba" +
+			"8f2e64b482427dfc535d40d3f58b4dee99053b35a3fed1cb245c711fa16f" +
+			"c141974c8db04f4c525205dad6ca23ccaebde585cd3bc91f5874452ed473" +
+			"08de95cb6164102744f90b3007e511e091653c97d364fe0cbd7f4cd3249c" +
+			"1f5c452becd722ccc8c6b4e371e2631337dff78efd903a8fc195a90ca5a2" +
+			"aa4513bc63cd43794ff06c5337329055c43d4fb547e63d6e4d14fbe37b52" +
+			"1411caf2f1b0df51a68f677db59aa227c725cf494ccb7f8cacc5a06ac5bd" +
+			"f135a2603175a5fd5e5af615fd2e7cea61934e6d938b9e672290aaccd99a" +
+			"7e26dc55efe928e56ae6354168264e61668a61f842a581cd0c4b39e0e429" +
+			"04631c01320857b4d7e260a39c7fbed0593875b495a76aa782b51fee4f88" +
+			"84ca8ddb8dda560b695323cdde78f82dd85757cadea12ef7cf205138c7ba" +
+			"db6a7361a8d7868c7aefa7aaf15f212f5f5ab090fd40113e5e3ad1ab04f9" +
+			"b7f68a12ad0c6db642d4efb3d9f54070cc80d05842272991bcdae54cd484" +
+			"9a017d2879fd2f6d6ebce27469dda28ad5c345c7f3c9738038667cc9a5bf" +
+			"97f8f3bc",
+	},
+	{
+		key: "aa3a83a6843cec16ab9a02db3725654cb177e55ec9c0c4abd03ada0fbafca99a",
+		tag: "719dbe5a028d634398ce98e6702a164b",
+		in: "643883153c215352a4ff2bb2d6c857bafa6444f910653cacd2bbdb50ffdb" +
+			"cae23cc297a66e3afefbd85ab885e8ccf8d8f4930e403662fb4db5121aca" +
+			"82dfcc3069bd5f90be4f5bfd3c10f8038272021f155e5de0a381d1716abe" +
+			"0b64b6d0f73c30baf6ddfe0e6a700483cad0fa14f637afb2f72361e84915" +
+			"78ba117e1c03f01fd61aa8f31da6464f3d0c529524d12dc53b68f4d4b326" +
+			"db7fc45c63f75244002b8f9a185556f8aab85948647818f1486d32c73614" +
+			"b8c4763e2645bdb457721ff3901327588da01622a37ccbbd0374fec6fd1b" +
+			"cce62157e64c4cde22c3a5f14c54cd6db63db0bd77e14579989f1dd46461" +
+			"4c8691ef26406984b3f794bb7b612e8b160374be11586ec91e3dbb3d2ccc" +
+			"dbfd9c4b52f0069df27f04853e7cc8b2e382323345b82ce19473c30296cc" +
+			"453f479af9a09ec759597337221e37e395b5ef958d91767eeb2df37069a4" +
+			"f3a530399961b6bf01a88ce9dfcc21c573e899b7951723d76d3993666b7e" +
+			"24dc2570afe738cbe215272ccedb9d752e1a2da00d76adb4bc0bd05b52c3" +
+			"fa08445671c7c99981a1b535582e9b3228ce61662a1d90a9c79afbdcfcd4" +
+			"74def2b7880cac6533ba0a73fa0ba595e81fd9a72ec26965acc0f4159ba5" +
+			"08cd42553c23540bc582e6e9ac996a95a63309f3fa012eac14128818a377" +
+			"4d39936338827bbaafad7316e500a89ed0df7af81be99e2f6aae6bb62568" +
+			"1dfa7e100ebca5c8d70f67be3c1e534f25446738d990ee821c195c98d19c" +
+			"fd901e7722b4e388da90b95ac0b5b5dc5d052ad6b54f6ea34a824bcf0cd8" +
+			"7f1fc9a07e8f5b8aa0793e3c9c1022109a7c7ae97ee2a2867fd0cf0f8971" +
+			"34b3d150d3b24fcf8323de929b73cca01244df02510393f0b3905caa0268" +
+			"7fe35f64391e7d4b30be1cc98319716528ca4f35bb75d7e55cf7749968c5" +
+			"37136eddb149a9f91c456fde51937c0f35e7e524647311077e6fbe7f3c12" +
+			"37b9584fcf3b0f78744c7b2d3b452823aca06d144e4463eb5b01014201cc" +
+			"bfed1adf3414427072135d48e705b1b36ab602cae69428e7c19d39cbb4e0" +
+			"ca26a871d607ed4daa158b5c58a0a9f4aa935c18a66bdeff42f3dc44166b" +
+			"a299d71a2141877f23213b11c52d068b5afadc1fad76387cf1e76571e334" +
+			"0b066ade8da02fe3b0bdc575b1d9ec5d5f5a5f78599f14b62db0bef7ccc6" +
+			"1711482dfa4787957d42a58fdc2f99525c32962b06492229399980601bd2" +
+			"ee252306b1464914424de9aa414a0a6e5dadf8ffbf789e6d18a761035d3e" +
+			"f2ff0753becbd2dd19fc1c28f9acebec86f934f20b608a9ef735ac91f6b7" +
+			"83d9327cce7f4870d39bbbfb0100838dee83e6baf2b40cfc98415dd174ed" +
+			"72e393ad0459e8035dce7eb18eb3af2f39d2712846b9e1852cd61d06dfc3" +
+			"5e34fb761b67e2a711ceb4a82557371ed32ca8db2e4cd7fea0b6bd026177" +
+			"4057b9abc45dae6869cab1097459473a389a80a4523e5de696554f8b0bec" +
+			"0ca605e6acfaa00386fb5a48e0f5893860a29f35e680be979cf3bf81ee7e" +
+			"ed88262dc80af042b8cfe6359cf8b475560bb704728034e2bd67e590bd76" +
+			"1632e516e3292b564c7265d7a6dc15c75ba6f6a447b1c98c25315ac7de59" +
+			"9edc4993e4dc7d1dbfcea7e50ebd0b226e096500216c42de3abe352e5b09" +
+			"a3c9754aa35d00883906599c90a80284d172a90abbeaf7e156fe2166ada1" +
+			"794420fe55b1a166d752d0eb7f04e822d021c615e84777101e7c9f9dd12e" +
+			"565b7d093fe978f85e6142c1ca26798b45f4b8d23ecff6be836e810e314f" +
+			"ebd2ea66f2ac95bad84b39b7a6bac41448f237b45e9ec579235ba2bf5fa1" +
+			"f00286379ec107c743f06ae0d11b57a2f5b32e3bc5f1697aae812d7ca303" +
+			"b196a8a43259257f7697bae67adc7f121be561b2d0725982532ffc06cb22" +
+			"839d9066dce0e4d683d9348899089f6732de62751ca77f1c439e43054468" +
+			"2c531b9c61977bc221b66030f7571dfb3ddfb91d9838529dbc99612f650a" +
+			"d72bb78de061192068941a81d6ac341101aeb745b61bd7a87a35a2714d50" +
+			"c3eb2c3ea148fb9ebed948307f8b491aec277ac01903ba36e6ad54f89fe4" +
+			"280a17f8e7ae639e75aec16d56576f03c2a1efe4af995eb825ccaa6efe0f" +
+			"d6d878299a351591d791c286cac5cb049834580d47a9bb7720d0603e3141" +
+			"ad7c1ec2dd23d3002e15d73c1828a7f08062848b1b6fcf816bd954743547" +
+			"6f0d6f882125bd03095eb1b1a846d535730e258fc279f7095de7c2d3fcca" +
+			"a4640a2e2d5ce0974c1e073c60bb78171c1c88ae62c7213a95d36ea9ab17" +
+			"59093813b85d17ff106e69100bd739ede9656388bf47cc52730766a8a186" +
+			"9dcc623e09e43cfba1f83ae1d9f16789064ec73504c29686760ea02c6634" +
+			"a929ca10c6d334b1751494c6d143671ce8e1e7dcc9bcda25af895a193032" +
+			"ce27c1016ccc4d85507fd2265ebf280d3419f54f66ba2a161c068491578f" +
+			"be056f02f97be745db443e25ed2647c5348f278f4ad8bf5b2a2c2d56e795" +
+			"532e25585984a3a94f435ef2742a0413abed7230ff2e9724187c91f73a7a" +
+			"726ebf36bc8d0d959418dd586452664990889358c56720c1001c004ff768" +
+			"54b9850890ce1b31735fd9f4a3640622ef0b25c659e8a937daa0df7a21f1" +
+			"77be13dfdb8f729da1f48e39a05f592d8c98da416b022fd8edab8e6132eb" +
+			"a80c00501f5cc1e0243b6b096c8dbe7f8c6ffa2f8bcc7f309fb80b489b92" +
+			"c4878fabad42d91876e10ee64ccd415124461cdc7d86c7bb6bcd9133f3c0" +
+			"dfa8f629ddb43ab914c0ac5ecddf4398052229876fd838b9ae72523946cb" +
+			"bba0906a6b3ef26672c78cb24cbf691a5ec869d9fc912009d840772b7da0" +
+			"c7f47856037c7608705cd533918c207a744f75fdfac618a6981778e09332" +
+			"5c7d22170da85bdc61044b4c397919d601a30746cefefa798c58f02cb827" +
+			"0d130c813cbeb67b77fe67da37a1b04bf3f1e9ee95b104939220fb8a0394" +
+			"86ab8954b2a1468016f546406d1946d531966eadce8af3e02a1f59043ff6" +
+			"e1efc237dbf4dfd482c876531d131c9b120af8b8fd9662cef1a47a32da40" +
+			"da96c57dc4efad707a4e86d0b84262d850b451bda48e630c482ef7ede5bd" +
+			"c55147f69e2ff8d49262d9fe66368d1e38ecdb5c1d4e4042effff0670e69" +
+			"04e47d7d3047a971d65372126ff5d0426d82b12b253bb4b55005e7a22de5" +
+			"6fa54f1dfcce30b1e4b4f12b1e3c0de27cea30ce79b08c8c1aceb1ffa285" +
+			"c317d203a9f2e01d542874fc8035b7670f3648eec79561d6ff2fc20d114f" +
+			"ba4fbed462f1cd975ee78763c41663849b44cb2827ee875e500b445193e1" +
+			"4556bcccfaba833bb4ea331d24a6a3bd8ec09906c7b75598b44ce1820a49" +
+			"fca4a0c1501e6c67515d4fa7f88f6aa3cd7fbc6802131a7b14b219e154db" +
+			"9ed241133e10ace40e4d963f904dd9f3bdaaade99f19de1ddfe8af2b3cc4" +
+			"0a48374dd8eb559782bea5410f8f9a1cd128523c0157b6baad9ea331c273" +
+			"311492fa65c032d0d3b513d23b13b86201840d51759021e4133f873f2781" +
+			"8f54f34ba73b4f33107d49c8de1533856ec37bb440f3c67d42148765610c" +
+			"3296bce932c839fd866bec3762a38406ac2b39d0d93730d0c88cb8f765dc" +
+			"d8ee71263fc96068b538da06fc49e25dbeaa10a5111a9af8e8f8d78e6ed1" +
+			"3752ad021d9f2c6b5ff18a859fee9651d23a7237bd5a5c29029db3882c47" +
+			"0470de59fd19fb3bfbd25d116f2f13ef5c534bf3a84284ae03e3cf9cf01d" +
+			"9e984af9a2e63de54e030857b1a071267cc33d22843b28b64b66e4e02803" +
+			"c6ab5635291aefa69cfeb3958c09d0b37176842b902da26caae3f0d305e7" +
+			"c6ab550414e862e1d13d9bb9dc6122cb90ddb1a7bc6d31c55f146659baa9" +
+			"6cca4ea283e5e1639967889543ecb6849e355b6c0227572097221dd46c1d" +
+			"f8600b230e9644ba611ba45cd83fa4ac7df647b3be57387b6db12682018a" +
+			"de9be50a8ea7d5f7c743bf0c6382964bb385b3c207c0cdd63279c16130b3" +
+			"73ba974125291673344b35c8ef9a33be5a8a394e28dc1448f54d46af675a" +
+			"edc88ce85a11ad7e50058df4f3f2364abd243683d58a2b13fcb0dc0eed21" +
+			"380b666eb87f4be75e7f2842bae916c15af3e9658c55408537b2301faa6e" +
+			"42af4d94e3eda6a41d6d302be281e2a9299e9d0fb1f20cf4ca978e66bdd7" +
+			"4c8bea0f15c84d6513cdea787dacbd4bb529ed03528284cb12f6ecd841d3" +
+			"c58c3a57c6bc19b65d6d10692f4e1ad63b091137c8acacc6bc1496953f81" +
+			"2972bf6362cf883bb75a2d10614029596bf9f35e92addbb50315b30161b7" +
+			"de8867a1393d9583887a292cadceb54078c9c846ec30882e6ff987494060" +
+			"721d3c761940b91a126e8d1e0118617bdae01a7f9c1aa96bdd6c78ca06f2" +
+			"6c8d85664a8705334f4997c724ef98fe265985593d5a9c30798714e6de1e" +
+			"bd04b648be47a6b5d986a3103e738a5cd114b19b7ba99d2e2eec6181bf3d" +
+			"ff0fec8c54ae6118be8702c3e775d493a6fafb509712a43ee66c3f4b75b0" +
+			"194c88937cffa5fa17b284d2556f2b0eebf876e05f92c065515198bd5e83" +
+			"00d0db432cb256a4a0f9963a05694ffce3ecbd182209e0b7bb50120f6be4" +
+			"eeb9d268b17790ee14a2c887dc5753e0086630b3123734053aa37595aa8f" +
+			"31968ddae4991af4ab970c1e3cfa1146a2efd9dc42abd6af14777b8a0455" +
+			"3865691cbac4b4417b3fa13c154d581b498f3b8cb77adf0e42dc2f2fb521" +
+			"732447de97271e542c6cf8cad3ba0148cc3ba1f2983ead836a25a2c022d0" +
+			"43ba18fcd009d518d07b53344a5bc4d626b3b38405a114471f75dc70e015" +
+			"d11e8f6f57d087fa72909785573008b1",
+	},
+	{
+		key: "1793bfda9c8666f0839b4b983776735a927bdaa3da99b13c9f3d1cc57d4d6b03",
+		tag: "bc89cfec34ab2f4f2d5308b8c1a5e70a",
+		in: "a09f661aa125471417d88912f0a4a14115df9a3a19c1de184878291acb0e" +
+			"89ee1f9d8213f62df442f8969a9a5a7c402fea09bdbe236fb832544e1f93" +
+			"9cdd4873802b2bb8fc35ba06b7ff96da6dc7efddfeeda84116bc525a7fc5" +
+			"2d84d2e63cbac00b122dc64f2d15b36595259d81a1d2a09f204c54072751" +
+			"dd812259df1104bb2d2ee58baee917c5d0aa2649c8a1503114501e6ed6fe" +
+			"239847d3d88dccd63d5f842426b600079c6bf06e80a2813b2208181163b8" +
+			"61dca07fa4d88254e84dac1c78c38397a016b5ad55a6b58878f99036db56" +
+			"89871ab3c321f6ed5895f218f8fd976c348b3f1269fcdf4d38c9492b4721" +
+			"6c45f499f5705830b33114d721f9731acf6c69fca681b74c2d82c92e145b" +
+			"7bab77110821d3a12cc818d7595a5c60c4b5e5219376c38a4dd52d435d41" +
+			"562802ff65ba2bba5c331c333d5adf194d29b2cd9ebb55927bb4ec17681a" +
+			"3f5574ad34fb4e964f2c756f6dbbb7a6876a21579a515263444de7a30a33" +
+			"15005458bc137ccfdff18a3892fc9f58f1de10d4de20bbcf860f5f036d8e" +
+			"8a188f18e5cf7ea3cd260710e7491befcb131d49a28dfb1ef688fd021a1e" +
+			"e4420d32fbfb03b47f5e85c37d91e49a1b0db85d966eb5434c4197433eb4" +
+			"9d56f2ff999c9a72230447032dc949202468261b48b6ac212e3f651d6c63" +
+			"03a06c90bb2d3a755ed91ba73bcdc28e1c5b0936e51e0a9f69c3ebabd3db" +
+			"add7abab6d8f6a44daeb3126429a01815f57444fb7022a4a510f8b564ae2" +
+			"dd9779b3a273fef15859a33e233724846c30d89fb78a595b6ff6c834812c" +
+			"00a991e405806aafd0c26a788895ad00a5e43c5426197aa8247207077548" +
+			"ee67db4cd6f878431a2e36e952d84b5fb89d681f553198e2c066310ea6ac" +
+			"3a31f5b1792620616f6c41d486fb844eeacc7fd36971abf416e8d6d50985" +
+			"c83cc92ea46ac37da8f0026aba30c945d8bb15080d2d95e4081bad626199" +
+			"3f95f57ed3252822a7caa035ae22a36c35e280cbbc82d729346cacdb1794" +
+			"ae9a9bb2793fd1d5c47121b135c2836063367339c5151b4e35278e97f62a" +
+			"fdd2f231d4b47812d083a829ebb9c374ff2ae8479cc4b76d55f9cef3ec6c" +
+			"4894f53e8caaeb0d8cd072960cedaf758e48e3640590d4f728626e0a08ee" +
+			"ebf719c96bf8ed4d0c283be09c0ae67b609e22d3b9aa6b03642854909de0" +
+			"5ed52b39673867bf586a632ab8072de15c637cc212cba8387515c9c9c433" +
+			"abd7ba6b02abd09da06a34694ad34f88515b65c0c9c247fdf9819fb05a1a" +
+			"ea4728c1182f8a08a64b7581cd0fb2131265edcb3d4874b009aede0e87ed" +
+			"463a2e4392aefd55e008eb7ba931788262f56e53193122a3555d4c08133b" +
+			"66020154b15643fa7f4f5e9f17621d350ede3dc70be02c59e40fea74dbbd" +
+			"7919d1a8d4e22ef07c916fa65e7d4b89fb11a7c24ddc4ca5f43344c753b6" +
+			"1331c3fa4558738ba7832b5b2a275bc9b7989b6e6888865793329806cd3b" +
+			"f0ba57c941d4428623e062f4ac05e7cd79ad5446f8838f2b247b66bddadf" +
+			"540845a1bb304a04b7edbbff579c8d37e2f6718f8690abd5231822c7e565" +
+			"69365ce532449a41ae963ec23a2a75e88307dc6b59cbb3fab913e43ed74d" +
+			"841ca9f6e4ef96dfd9f04e29e89361aece439c0b2e1943b30410a63d495c" +
+			"522ac3ec1b04ec4cb345f7f86969957ad750e5bd7dbf1d6a22eed02f70b8" +
+			"1cb5b2b020c0694d7f63044f9de0c3de1ede52009c858992d01ebb92ff19" +
+			"a9e0fbea18942fbafb77746c8e9e687dd58ccc569e767528bde43b62c7c1" +
+			"270a5721f1212de2b29a7aae2d6ba6cd173d7fbc78aec4356ce2e8ba9164" +
+			"d97dec061dd0c3a0e3c520a7611ac99739049dd5825537c70b7ef660046c" +
+			"1785546cd99aa400da848eb7c3c91247415c8e245d0f14c30d482c5849ae" +
+			"aaeab2568288229b08267818dae8f76fc674c684c99eb5faf88a0783813d" +
+			"f7298e0b50cb233f78471e5ca9cc3b04927c26a3871cf253798cc49aa717" +
+			"d8f18a1ddcbdc26497d188f15f86ec494dcf8f942c3e07e572385c6fa0ef" +
+			"40c0b625f1737543074a747a369482a0b342a08b3eccac9f9209be31aefe" +
+			"5a7794974f71ac0bc9a58026397ea3dd4f5e40511d58d2a3b45925c194ef" +
+			"13987037d736dd48b509d003a86471d5f161e0e5dd168b4f1ce32f703b89" +
+			"15004d8dfc708a5bb02b2e6fb67424b2cbcb31ddaa0114c4016b0917382d" +
+			"aad11815ff5b6e37d5af48daa5ef67cee3439283712bc51b5adf2356cb2a" +
+			"5181b8941fd78945c7c9d61497683e44fee456ad345e12b4258f15945d45" +
+			"b6ca4369ee792d849112d583fdb39cd4d333ee057355f0abc8d1eea4640c" +
+			"128cc1617982db0394233dbd416102eec1874081247d2982bbf9fed1b1b3" +
+			"8f4da923d68c8975c698f189a4d7840fd7aca9dceb7d91c076f85e1c546f" +
+			"4d5de4f60c91348455aaea30cac134c844dad93d583c139dd52b3be6346c" +
+			"4d2e6864125c5a2d0aed8f67930e1ebf8700ca88aacc914ea76ff17148f0" +
+			"777738cc126e75a2c81110faf02fefc47c91edbab7814599000ce55fe20e" +
+			"f313566e9b62457acf2f22e1141e220bd9d4747417d03e703d4e39282803" +
+			"386327fc65dd597f723ee28185c78d9195fc70a75706c36287ab9c6e00e8" +
+			"5cecbbd6043c6af8d30df6cdd8777be0686853b7c8a55a5b1e03e4431d39" +
+			"1725ff99875a85cae6926998723b36d13ad458220712209bfc5e8d2ca5d4" +
+			"4ea044d5ba846b4035e7ac7e9885f55d3f85c0c1b3d09fe929a74450f5d2" +
+			"9c9672e42d3f59be4ca9d864a4322cc454c2578493bd498a51bbe960e657" +
+			"3e5dd02c4a3a386d4f29e4578a39e9184024cd28d0e86ecac893b8e271bf" +
+			"ce3f944d130817378c74d471bd20a4086f2429ed66c5c99969fd8da358ff" +
+			"5c3be72bf356ae49a385aa0a631b588ddb63628fd162673e915cfc4de56e" +
+			"ae6ff7101df3b33125c9bab95928f6e61c60039b6cc07a66f9c733251447" +
+			"ef9c1ffefa2158a8ddf89dc08686a4cf9b86ea09914e79842d72a3236afc" +
+			"98a3afa0a1cac5590ab6a923e35a2ab8db6410a9d33cb84d1c48a054377e" +
+			"549774b25f50fbb343ecd5db095155cce9fb0c77d09752f62d4bbf16a770" +
+			"30452a75f6bdf73f7807d8f3a6bae16ad06b22175fee60549c22548de9c1" +
+			"3df35ef4e7bf7b66491a62b93c2c3fb0c5edc51f60f5704b56af30f1079d" +
+			"7c385b99f958ef8209e030e381d1ee8d67d3cb84f32e030e8ea2c1d0c77f" +
+			"d6b242a9f48707557c8682a08e1127f51221a55c733ab1edd00a9c2912cb" +
+			"36dde85f73b524e1a4f4da6414c5e4c18d9537722b2becc8a91bcc63f2b0" +
+			"9f32409c53c2beee0de6726dabcd6bf33118a5c23fb9c5c1810476efe658" +
+			"4bb6109c516b45e16b2f79f96755680374d82b91f2c519639a1815fd485b" +
+			"a3c00b46fbefeafcf25554ec5a6a5ae2da07c85b8a0f9fcde50263d9ed85" +
+			"038b2f7aadb9de765655bd201235218bfc74bcad6a9ddf4506167a649afa" +
+			"df400b85752d68a92b7a97f26b334dd77fce824862046b286a7c8e0adc36" +
+			"f713a252a673d4d995b268badf4bec8b8eefe85c25b823b6728582d35c4a" +
+			"60041114dab72b0623b99e2758f6a1e97365279bfba0eb1fc8952ca4f2c6" +
+			"fbffd9f5fd7dcad1125b18a796981b5ead0b6431141315898ace96f0d38f" +
+			"865698df8822ca7b65644b6b1f0a0f0d2e5850d4c93ec48ca3eba1b919e2" +
+			"4413a46d595ffa427715e499db3b7b9ab53c64abec7302bc737a5bd124bc" +
+			"da756abbca132f7f67e6989e09bfb23b497da31bf156bb9c69ae54588df1" +
+			"7420e8fe989f0472c8893b2bfe57cdae265a8cc7aeb39624167a567a6fbe" +
+			"bb1aa30c3dcfd14f2808a070994085e6e1fa79021e77c399f90ab1f995a7" +
+			"baff672cb693bd39b798b4c890b7d0a57978d6b9bcdc5bf3f4d205f8f24b" +
+			"2b43d3ae300a96971c9182be297618b9adceebedba1ab0f324b01d23d7e6" +
+			"35f009db3dbbc643c2d787567594bc639bfd78c4f3e6d948caf06f013423" +
+			"eb3c764666b58f886d5d28137c053c2a28535efcea400147e92ac6753574" +
+			"3b47f9cb48852abed1d057647d5b1c6f334eab1a813401fccd3dae332738" +
+			"776bb223e359f3c459b5c573ba64fa945bdd66c5ac0fcbd53b67032a7b80" +
+			"25f551e8d1fd2a4291bdb7941cbabe3a09765dc263e2bbb6db7077cc8fe6" +
+			"790d4bed5e36bd976d1e37dfdba36aafcdaa10c5f3ed51ba973379bcb8fd" +
+			"203d8b7282abbd271ecf947e54486e8653b7712c9df996a8ad035f41f29c" +
+			"ab81509f922c67dacb03f25f8f120cb1365ab3c1c286849c2722448ba9bc" +
+			"ff42a6b8a7a52f2c79b2bfcbdd22ef8a5651c18879a9575dac35f57d8107" +
+			"d6bece37b15d7dfff480c01f4461ef11f22228792accda4f7936d29d4c56" +
+			"cbba103b6d3e6db86e39e5f1bb9e9fd955df65b8a6e44a148620f02b5b90" +
+			"b2be9e5bb526d0ec75b1e723e94da933a356d7ca42d0ce8349699f730b8e" +
+			"59bac24a6b633759c88041d29399ce60a2ca2261c7eec1acb9a56e0e65bd" +
+			"e37653ce2cf7eb83a4d019c755bdc5d685b6394ecddb9006823182dd8138" +
+			"a1bf79a32d07a8e5e8ab221995c714e571b40bb255b79e328ab883542c16" +
+			"4899fffa16eb3296f310e302512352a864fd809beaab4169113027c6ccca" +
+			"99a92c6ce35c30f9449a3add70f10db1ed08078e8e6cbaafef630aab7e9f" +
+			"c8adb09c18e33fe1af3620d1e4d069ac11325e23cc18e5519a1ed249caf8" +
+			"ddba871c701f1287cc160019766988f63e089bd9bf1af7e6f5b9002e3b6c" +
+			"264d69a8bac16914ab55c418d3a8e974677cdcbea36c912e90386a839a37" +
+			"77b878e680c07c7cc99f42a7dd71924babf7fb0627d1f2cc60d9d390d1e1" +
+			"50d47386be6eefec9ddbb83b28fa7e2fd28cc3867cbe42d13b00545af8a0" +
+			"48cc07016ec79808b180e0b258c564739185da754f2e",
+	},
+	{
+		key: "0d41cb4ac25217feb20e86fc2490e8d2ea2e8225c051252a9395cc4f56e1ae5a",
+		tag: "42df9f9a59d6dc05c98fd9e9577f7176",
+		in: "01caba7a19cdb09dc0ec6c522c61c628eacf17ef15485aa5710fed723875" +
+			"2e4e8e93dd4bbc414e4c5620bab596876dfbea33987e568ddabf7814b318" +
+			"8210a5f8d70041351e4d8410840642a29cc8d901c25fa67cc8f9664ea5e1" +
+			"9e433eaff7c722d0258ae112b7aca47120aa8af4420d4412a10732551db2" +
+			"cd3e0af6e5855d5eea61035af15a4d0d898d04033809e995706eba750a7c" +
+			"ac07aaa0dc71477d3020f778d0347f1a8e37c18540deb9ae967e734c0264" +
+			"df0e1f52b0b5334805579ea744c8784c3ae0c3ff8217cd3f53cb747f6996" +
+			"f3d2147699799e649061b205f97f7992e147fb20f21ff862c6c512e95534" +
+			"f03075e8e52f162e0d70d7a259e3618474427f400f44f75198edebae6e40" +
+			"a2173257d114e1bb5a13cf419c821eb124d90e89a938d91f4d2e70dfd1ab" +
+			"60446f1b602614930a329e98a0c30f107d342281db25b8f8259933e14d20" +
+			"8bbd991e42969e8b0600272f9bd408483cddfc4cb8dfe7bc19be1989c7fa" +
+			"129d38e1078d094b82e0a845040ddd69f220dc4aa2b236c44101d7da7779" +
+			"9827a7b037561b51e50fa033a045571c7267af93b96192df3bf6180c9a30" +
+			"7e8c8f2b1d6b9391767369625015da02730ad6070df4595eb8099bd8e484" +
+			"59214310cb62c3a91a4fa8ac3b3d7b2017d4254fb465f0a248e1bf45819b" +
+			"4f0360f37c9a79d405e2bb72e5c25a1b4df192cfd524d61e1e8b274f2fe0" +
+			"634c73f0653c7c9e9062c9d081f22a8b0327897eed7c6e870f2815bbac8f" +
+			"585c1bd868759a98dcb5c3db2f6c53244b9cc494a56f28a9ba673167cea8" +
+			"b799f37049ee7b0772972b3a6603f0b80eddb58ef03f916106814d72f000" +
+			"250b3573c97c5c105910d79b2f85ad9d56002a76a1f43d9d1c244ef56d3e" +
+			"032a9bab95fe3bd5dd830ad7d7e341f28b58c0440658f7fc2ca98f157708" +
+			"1c647e91432cb0739d9acdbf973ceb9b0047634d695279e8837b04dc5357" +
+			"f013fde3c55c9c53bf1d817ec59a1b18ed0ac0081ed9bbb3bcd1a5d3634f" +
+			"50f7506f79dc6a4ebfa640bf65682fe9aeca68088e276937669250064de1" +
+			"c19ad6d5c697f862114d0f81d2cc52be831ed20d3aab1e41fe6f476b5392" +
+			"af4799392464c51394c2d1a8325ee2e84f1635d295ee663490e538eb338c" +
+			"7126a8e731ad5c0becf144c7a9cae5c6493350b589385de29e1a0ad6716c" +
+			"346ec4f0a31ca5ea35c59ab6b099f65d7f0b3d00925a1da1b5777c029aea" +
+			"9679e895d7100645dc83f81d82a6174beab2357f7888ea640900cf3ee67a" +
+			"e0724a123919d78e70e05288f67e5e69ffa6f345be8a96e58bbe260184b5" +
+			"ec5c0c1354cfd516ebdb8d420029137d41b029641959cc07fa7b4e16b39d" +
+			"17f36b2367057410a42e0550e9ec1dcd2df4604d52d4f9dd1140d57af08d" +
+			"50e1527dad793b6d649324de799754f755818bf10e6d1ab614958dbb24ac" +
+			"8e2c01270a90ec3df4379c3f509b5ef721b0fd4f91a1bdb8127ae4dc74d0" +
+			"75f6cd8bb28319d6f8e8d8ff64fb4a42d646e9365156c6bc72cc46e9cd1c" +
+			"f9e735549e3df9a8e6b5fe541948b126190117db71fd1d61ad84be0f725f" +
+			"20b99eb141b240326d399976c4f2ce5823d94649a9580e1e8820bf49184d" +
+			"fc34378a60bea89b12aca69cb996c17847b7fb517cf2d51f16d78e3875ce" +
+			"aa33be15f6a154004f0e1134c6652c815c705efc34bcf35bd7743d28f0a2" +
+			"77d82dea4709dab41fbfb4e0cbc118c17aa00808872f0edc6437c357cd31" +
+			"74a02aee61890464e03e9458853189431bf5df6a0ad5d69951e24be7f266" +
+			"5bb3c904aa03f799fe7edc7bc6779d621cab7e520b5994f81505d0f01e55" +
+			"96e14b4c1efdf3e8aadee866c5337c1e50066b3acc039c84567b29b7d957" +
+			"683cadfb04fb35402acaba631e46ca83dbdd8adf28e377ec147e4d555a21" +
+			"e6d779d7c5a3078ab72702234d36ca65f68bd01221c9411f68f32e16ef04" +
+			"99a20c2d945fa31b79d9965853d38ada9d48eead9084d868c6bad974b0f4" +
+			"0956aa0fcbce6dac905858e46c4b62c0ee576b8db7d484a524e951f4c179" +
+			"decfc7d6f619e86dee808f246dd71c7e0b51d28bc958110d122fa2717148" +
+			"77823242711632f6e1c7c15248655ced8e451a107707cec8c84929beece4" +
+			"efe5503d3c1763d0ab7f139f043e26027d5e52a00d5414dd98a324a8fc2a" +
+			"06a1345cbde747f41099c3377b86bbdc5a17c8f6e5b773a761f78573832e" +
+			"4359b143810361dedc79142fffc49ddc0b32f225d50d360ceec3920fb0ba" +
+			"0693b644ee07fbd1ce829e223a02794b197614061c4bfa46112d105c2b7b" +
+			"4efea448501d146dece44f6640d674d5749db498b32969de6e165e705a18" +
+			"2aa1f3d8e16892b0120337640d52c9bee35e5b4b17f03eaeb31205c8ecbe" +
+			"1ae1b110023016e40ee87370a65c5c20bfb00f100d3c6c1de6e4a1c90162" +
+			"f25bddbf300ed637330206788a4ff96903f971c9618493ad074412af625c" +
+			"ff9e0f8f183bbd5e96c1f28307e6cae8b50cc0eb1a3a8154e44e9de947af" +
+			"002e4d1098d6b0ee3f2e71a10d03eb444729c42461283f37be8af2ce81ba" +
+			"bac246a05c2c94efacc43f0cf9ff3df38ab6fc1648c796ae7026ea95752e" +
+			"b70873a6da59da10d8b5316126431c4a17289466e95dc739c061d7a4b13a" +
+			"450809479eef421bddcdade77a6df133410328c754af8999a09b1a5c056b" +
+			"ecbb6fc2c339586ab92100f46d2fa1fa689994b36aa70703d76bf7738adc" +
+			"f0589fdfa6bd215339ad69ed983f62efce0add5a63fe7dfe4bfa006ff16e" +
+			"0cc06d39199ad60adcae12b75ca98d764502a783373da3a41281e03c2037" +
+			"e1b3ca7f7eb60e2b67427e97ec72d36670db7662c6daa505701fd279f116" +
+			"ac0ef569471f204e1531c25a4ac3ce19b6f68a8994b6f89b5abf034a6507" +
+			"32c7fad4206eb4eaa7cd9a710d866bf3c3f13c16faa268ae0cf4f69be909" +
+			"bb9b79aab80dd25101d4cc813a48d3f38d870f10ac0b6768005aa0e69e87" +
+			"dfc0424deef06414c9ba6f498c93c41c692a7a6221fb5595b390a32c70e0" +
+			"2cd64471c797ee8a143725849c1e054ee2043dcfc0b4cb1c00be21a14be9" +
+			"2d9a07f1b4e975d4c86b8a5c1387e6c42bf393e078fe86d24612d497e14b" +
+			"874485a3cc922b5b6d91295d7b79ab8bfa1c7f64b51e761d19bb9da82a5a" +
+			"a34aa469699036b6b2c55e2b84f84942f10585027ab07e2e0e562e0fc3dd" +
+			"36047850ded84be4416e22aa41c7a2f7d4a4d8e3dd420d746a1d8d56d87e" +
+			"5133a1b4380bd9a89500fd6d7e68a1ec02eb9e79e4a13edfdde1273466e4" +
+			"6b0e6a75f59ff6175716629da52463ad21de27f40fa2e25a566eec4b2696" +
+			"4af3a717dfb0170a73144c0bd9b00bed67ad8c0a146eb5a055812d071209" +
+			"c9d530cd4f50a41488c2238898dea8bb36b0f1496d3ea8c4ff8e263b367f" +
+			"64977679e697d88e5295bd97ac16a0420850d1ead9621e25a3f58925c266" +
+			"ef5246488b1c15a8fe0d8ec4291864faa5a67b2388b7786f47b6d27e8fe8" +
+			"46f85f85163e54155ef95cea4901e712a44404a4d3f27f28dd961ce36b84" +
+			"f3856770f07f20a2ebd34d77405beab04ddfc09770167d7d6340f494dc6b" +
+			"7e4c3df896bd974730193b1e862b58d4a5938e6e4ae8897dba8812924379" +
+			"e54f51a71364d39f76e24fdf2c6c704479ce85b456558ca6947b8fd76f03" +
+			"78273f0a7bcd1d860ef1defe4eea8fdb81c73eda028d82fdcb2248582ac4" +
+			"59eb7698a811e6c5823be886410f6b8577ff2e8252343b6ea890016ae846" +
+			"01c5894cfb988121059fd9c8fbc1596da470a149404fc67baa15383d38cb" +
+			"d17ac107b4ff3c1ca4c76b7930de02b240e7547d39f4978e0cc1fa37f8c1" +
+			"012b677f07bb4df4486196e9b0beb823a3827585475b878e3f6f0a2d3836" +
+			"2c7d34f9f3c91ed46c39cec95c2a0b6f0279a03a00ed5035b0725c393849" +
+			"cdb1ed3c0ecbcf3c2ce108017f468e1c3d469c03e8231d4195344ced70cf" +
+			"daa667252cc1554dce8d0c54eb4cf4da62367d77d7dcc02f81e788ce9f8d" +
+			"d306ba1b48192359cfe92bdbea9980f87ea0677d7d2082205a436cf514e6" +
+			"fde5eadd21b13dc836ce33b5dfb6118bcac79ae00fbb16d61f00a923b145" +
+			"f9caa9f3a2c7f0104f8b052e390987e57c8dc80cd5f0358afb0111af1fc4" +
+			"e31f92bd832ad35fd2e0bdf768272de52ce0b152f74d43a8973ad516b3ea" +
+			"f5937ec8a236ebc86adeba610de0cf7168453111f3c983b64df07678cae0" +
+			"a75466ae15adfb127328e716448cdbd2c1b73424cc29d93df11a765441e0" +
+			"0eeed72228e1099bd20569d9d0e9e5a0b3c11d0002e2896631186483db61" +
+			"c1a0cb407951f9b1ea6d3ebc79b37afb5a7037e957985e4955979b91fb85" +
+			"61ca7d5e8b9cdd5b7ce0130a880d9241027b011fea7696b0c695d4949ca2" +
+			"d0cf22d44b9fee073ecaef66d4981e172e03ea71a6edc7144393bfea5071" +
+			"2afac137f091bae2f5700bfb073a6d57fddcba674a899d7349044a10aadb" +
+			"2e7f547887dd2f765f394de5dc9ef5dbf1eab4d869be8cb68aad8e2614ac" +
+			"37bbf21ccd5a832ee09fdd07ce50a580a2af36256b1046e646fe3dff6d20" +
+			"0c5110f1ad1311bc39b8114cd11ecdb87f94df43d4f6468932fc0ed892d0" +
+			"3d8f3db3f8323ebb29776ab7d260493a36700bcda668abd62126a8189e91" +
+			"df2d2970ef688d4e8172fc942e69ba63941a36b79ac546fff38f5f7d1176" +
+			"57612a662ea38134e1090c3e903c9adacdeefd3ac2a0467e9f5125058c19" +
+			"7b2260d2afad2b0e627a9ae52cd579ee27168065658089e1b83a2d8cdb47" +
+			"e08966e4ec0018e78c4d267f9575b8fea2a42de5c2d25356fe4b8c9cb1ac" +
+			"daf0d1af4bf58b9704cd4bc08471e3b9a0e45a5693433ede2eb1374bce44" +
+			"1f1811cdc7612d7bb61f4f34aea0a44757bbcc12a55c1ba41a7901eb004e" +
+			"689587a38e5b4df4574ddcc7b2eda97f6e480d7d39f45247ea3b03c90a93" +
+			"0dd168b65d52a59ce9c2cb4e860cc6aaa0ee02a58d0c8ba990194bce80fe" +
+			"8c34ba5693fb0943ec2cbfc919e534cc47c04f502b6c217c2f860d1d482a" +
+			"a016aa02adfc2bea3171fc4e27e2a262fd37b824099aa227fccca508f778" +
+			"b8c6ec7aaff1d15f6497753f439daa9e52060fd6e9e056e6843d770fb057" +
+			"6d9e2e782db4843c0c2c7f408a17376719a3c5cf9fa08f04f8a779885a16" +
+			"5cf93ce404be",
+	},
+	{
+		key: "ddbd5d6c5ebd61fa72b453dd849dc302c98a0f3e300f4768bf1dc698a3827dd2",
+		tag: "af608b71a353e63c64911558baa122f3",
+		in: "c67e2524b0de16483158a0232078fadcf611e4fbdb9e642e397b21222423" +
+			"cc2ed42ed34ffcb178448919ee337eff9d7d691f622e70fd3317cfd271df" +
+			"fe6a9d9b7e07db0d20813e2331164a654386db2ab06ae2983bf2460eaaa6" +
+			"3aa0171fb87afb82e85b40d95c8993b2039d32e9d38473dd13f41fb1ff1e" +
+			"261752ab004b221a4472b9b1a0e139f0c999f826a26a7e7df362b0611aac" +
+			"fa83c55cca2f7c0138d2c30313c2f6eb357278328ea6ebd6a5077947e18a" +
+			"a97c34b9dde3b6f2de4b83778ffcebc8c9cb58756691d5e2a3d15a759a2e" +
+			"5050b6da937a6f5551aec069a08027d60dd870d175d2a5b5f0b4f3143904" +
+			"7445c368a5c866370e9426abbc1a1c5a272b96731c4128aedeee93e8e00b" +
+			"b450601a6d31ea279b9450e738b4a47c0dc22d2d8ed5d44257f6318e0c59" +
+			"b951fb6b57746062ab95cd73c23ef0a5c000a7d14c18bfff172e59b6f6de" +
+			"aa61b81009e803eb05e24fb0b706870e18889a9180ac16a042d12dfff9d9" +
+			"1b88130f045d2342fd5ddc5f443681c31090459f262d1a65654c55251fc7" +
+			"d5a67bd2e62940ccd606f3e50700e4d1e992a3fdf0388b9ce3df9de6dda1" +
+			"5c1cd6b70622ac062dcb7ed7058872c00ff3df94032853927126cf6fa4cd" +
+			"c468d91c9b52dcbc272fd7ba920dcd3ea1e048af9c3286dba74d988ce9ce" +
+			"77174e25a87935352721dc23b60a9549322fadbe6a00dd1197dfa25b33fd" +
+			"9e5713afcfd0fae6dbcf27147fa58d995580d7e0a903c895752fe9819f5b" +
+			"b002ed752719552d0f3575312f2e618173a8ae7c147ca64a709053e5d2e1" +
+			"2f4d1ea337afa9ac4f9ba62760046ec1e48f4ed8f6df66786c9fd9f5bc7f" +
+			"9ca2526e1327b042f4657c405757690e190c91f260dee2dd3d2e6616b721" +
+			"e489c7c3cb828478a3d953b88f09904e7927cdf6dbd6a5419eeeb83c0be2" +
+			"51934a80dfe61e09442f0761aa2d013e10aeec3a32df204571ce8984a430" +
+			"9bbe30ccc91977790bf0305d2651ee450b749c3e7761534e45970e70a0a8" +
+			"473cadbc88f096970c275f188c9d2644e237fd50c2e24c1eabbf7578e80e" +
+			"6500762ac513fcd68cf6f8bb7a9d9eedadca059d9ecec07fe6fe7792b468" +
+			"9311861728dd482f087c28374cf9c5ea20b2c8630029e8485fa6fe518c74" +
+			"ef77d44eb7526ca764e50b5f34ed0f253a91fb2af6e59338e2af6e041e01" +
+			"084e1efade1aebb7d1b698ccdb8b4248ac89cd40d9517d840960c08f5e86" +
+			"88d8ba2b54889c1870d315498b70e0e9720f2c8c53a3377a8c0bd2d6a1c6" +
+			"f17c6ff847eb14def6855dc3886b99039e528b421ccbf6064e39263f8f3d" +
+			"340d5d20b1b14c264ac2310b5f3a0c6f0c1006d0d4f1a69af68d28ab447f" +
+			"cd17387e1fc98f164982a6d05dd32d6b4f0f1b04e40c6c6e0fb4467dd6b1" +
+			"0c5a9c92cc8c2bc97ef669b6d55cdd0aa8a15c46af954359165949012713" +
+			"4ea9f74181d54a300d3172c9f01db73288ef6a709c763a4891666d0baf88" +
+			"8531dcc77f0911412d096aef9033fa36b5c1ed283b8b5c109e45b5cde911" +
+			"6f3da2533fa0ab81929bd5783271d5501a9e4fce2aff9eb5a70a4215b253" +
+			"46885d7e4225fe34bb55b309a114a312693d60ccc61267359a8c2dd28141" +
+			"226e7cfd99f0f12c69df57d75dd790dbabfe3145f7fd1a24fa58e03bc2e2" +
+			"6ea19288af4929e5acc517d8f52a074745ff4644d94179eae6ba7d267292" +
+			"bbd2053167a0da9be5e4b6cd0a4200fcac5182d9957dffbefa857e662b82" +
+			"fc3a7cc32506e78030ed5c5d448d7f1b4fd854a735a0c50016bb85e6e716" +
+			"0f87527bca0de235f4b7dacb75be84919c15a5b8cf6bec035795cb67061b" +
+			"7855c2134c1b1bfa6affe04b7db239f73af6ea9c02bc9f7972b7f6400b6b" +
+			"838f4653aefc42179c21765e3ca7a5e96b4402ff544d4bc2332756a23500" +
+			"11241dc42ec6848afe127c00b9c333e69bb5a54ea5c7193e59ea22bd6d32" +
+			"af4f56b1bd2d5982ef7d9c1b02d7668525e4e81b68a400f7afc2653f0f41" +
+			"a03e11c7a02bd094830093481afbab96397245b9f37a568ea1c4ae248cdf" +
+			"afc87f88b1fb5dc300d8e9039af4e6e701b458ed3f32d693f2e869b76bb5" +
+			"1358cbbe5b5089013bf452734388a176cccfc1ae9b7cff603631ca48e129" +
+			"b5c9573d4e379547272cce8aeeeb407d3fc57f782a0eb5fcbd41e6fb13be" +
+			"7e4f1067cd407b42a6121b2969c384916ba2b32563e659f52aae09c8ce2e" +
+			"3c500fbb7e58be74cc1592dcfacd9f0d4cea1a90a18658147c81cccf6fb3" +
+			"078ed27f369e7646f551386a74e1b07074d93e0c1f298c761af46cdaae9f" +
+			"f4be86808b66d0e228016d27a3a77c843365cb847fddccb0bbcfb3b9008a" +
+			"1bacac59ffb0aa759a0568c72c556caf0ac1091431b574687c5fc7bd486e" +
+			"963e0fc3bdc828d988734a21070747c955cf8dba2df1c3a0ba8146cd58b5" +
+			"91b6d54712db67a9851b1607c8445bc97406eeb7488f5f85e547850d619c" +
+			"407f97632ca1801f52c09c2b314b4ab0f8e7fb5851fd60852f4666913ca6" +
+			"bc840c1ec8f8f06caefdbfbf02ce00f20b87b14ba9e651c80f40a31d0306" +
+			"403f541776075fbf23733a6b19e3b44d04b455b29ef8effa70cce0c59331" +
+			"7119abc07aa8c8d0246a760b0b36a3d87b244e83bae8a745b8277a531298" +
+			"f5d0283498a509c89898ddf0f7a7455be1f8a6889c46d323f1dd18c3babe" +
+			"1751a05f871f0639f50967afa46c19cb93d9c2a79c81e2436a7a62f225bc" +
+			"37c90698640f5b43673e1dc276de05ff1e29acdb4ace5121659db5f23c49" +
+			"57aae22f53e6f2cc935824fbd07c2ac87672eeeab895c3f06e09e178560e" +
+			"2fcfa7097f10201dfb8b1ebac08ca806c1b3ba3aff9284846a1a3beada53" +
+			"e9f7ade12eb89b5591f462b2543bb4090e081fee9fb53bbf821dc92d6b16" +
+			"fe820ab2ee4b1f6c0b6a6f19edb0bf6479e257fc73bcd60dc2261d0a4752" +
+			"e23a0be18abf355f3065177d8c3c14e21edc178d0abd1b39f703e6335131" +
+			"ec90cba3d9846cee7354a06c320a3f61b8a269abc7138831614f57ca6c19" +
+			"a4a621142889cd924bf4ffb82b57f871b854f3157e8874c22d43a5726900" +
+			"bafbb8f2260a1eba3a462e23d4def2ccf68ebaae8e52739a1ce67c039eaf" +
+			"9a6c3232fbb5a91d1e59a8dcd3798ba71345fbf83d09b83b41cc49d5ff5f" +
+			"2e809d2b1d5fbc1e7001ea76b9b2d8f896eb6609e2e1c5c562d2a6e74960" +
+			"2d67a0f6b43a201d5087509b8dc7b0440144e308c18ff8b96b607de2f20c" +
+			"6ee99bb05367a8b25947011889f724965a2b5c52c9db1e0622df9343c548" +
+			"d054699badeb15fc41055af0d79a2bfc1a5b4574634fa0dd9dd10a6213ed" +
+			"b6991187dc560facdc27440456a0a209fd7f5ee4fb350ae71f869723e5eb" +
+			"5338e3d1448bc993afca6957f4cc7b047a2c7c9593b7234725e66cc0eb23" +
+			"3824eb4cb905701cc522ec210950b871397c6c0bb3d0b839f2eb1a120f70" +
+			"36107246df4dfb2c24891bef0bd1dc131f2c9d7c295ee967e3184d963037" +
+			"fcc9e0b8c7011c8e04b4e70038150d34caab4f8c0230418cd2d8a91146e4" +
+			"4e11cf6707452ddc03d9b4e6380658135dfb48f62c0690ebad75167f4dd1" +
+			"c0df3ed555b5081a7b82616d9e501757c83c2193d0f640236d59f9c97a4a" +
+			"5c8bf532aea2cf5964ed2dbd8a70c01ca5c7677224cf2a37f3b24d8fe4ba" +
+			"91cd3b5033715de227de51deed15afb8eda9d2b9615d197b8f98322d7096" +
+			"79c5131eed48050fbe0145a9284e236605c25a4876e2adba42f4e35a8949" +
+			"3d59bbf44b3338d9d2e65a7d7ec6c863cd47cae9e23181b07298078a5e9b" +
+			"06a5c7e1059f474eb1a4247e8f02cdd4efdca67d22035b12abecf9b15982" +
+			"de4932a28e797bc4de38442cff2cba263eeddba0ab14fc706dbca04eaca1" +
+			"b4cc13000a10e35b32461424809b299798e4d8e66c92aa3181c5df16ab65" +
+			"9611cb625e895a8021af8c60960227d6f2ebeacb17b13536a5ff139734ef" +
+			"37cb67018ef9a410b856e6f6eddbe3f59b088d538c50a8f3f0912d06e47b" +
+			"88d773069aa759cc614e1f53cf6e572c127123d1ab56b79ee753a921cb22" +
+			"a60e4e6cae768c9966de4e2625484f2e990154da7fca84b6e6c0b59201e7" +
+			"fb8a729cb20b4c774381e84f1bd6e304543d952dc76ef741b72f3a4ca7a6" +
+			"ea7958b8b6337994ed82dcf988eb70f509610b9a279ab4d0f28cc2b2dd99" +
+			"3b8637a6be0cb4b5f67c79654c6b15e1b61120374ba9b974a628c547f11e" +
+			"52d72d39f8f9c5dbfc23a89f22d38984dd8d5c3ca72cd54e6adfe2b3d163" +
+			"86afdb50967846a4c311351a51e5fd322757bdb061d44c8796a61fa4db36" +
+			"793bc11984eac83bbcefb40d0bc7bab0ca81e7df3a7f58c6fe800396716d" +
+			"832acaddff6d72c8e19dc9ea838294ead800deadb6bc18d3e399fa76c46c" +
+			"5d88ee72a86a87399423b0578eb6e27d78156ea2abf6f08b5cbf747f2f74" +
+			"5301b694bfba84bfe3c5527acd50660eea5105a2644c1aa92f954a604fb6" +
+			"a1b3b2d0331497deafc3aaadc7040b9188a36cf607ee85a0655ae963fd32" +
+			"91dd58f8bb50b4e46dcf7c2957639bffa6b12d895660dc0323b7a092f999" +
+			"813380b820e1873c60d3e3038129c66d507862100a5d5842150869e7873d" +
+			"6bb6ad022350ffa3813aca26c80ccae72692bed9c77c9d4da23178c57153" +
+			"90b5f4505240a796ec9d10a7f280bd60a570b1b693453807707651fc0464" +
+			"03e4768965a6f42f112152942134f0a38c84137c7a6e086ef1ab9ad20d24" +
+			"3b93356b305c0996ab7d02c02c44cbaf8f7e60b8c0b8c9fece3f189b099d" +
+			"dbd126b7357c1c4ea1c8bc1ad93db91ea9bf043a4320acb60b502bec37b8" +
+			"6b2a5004b8225e549e613c6f83b97b7e4aeda1b013e0a442d7ce2f14e78e" +
+			"a94bab700c9ac0abba945e28f39fdadff223c4498cb204f01ddfcb450a41" +
+			"f32ae47f99a49114c6646a5cb103e9cd75f9d81dba417e48c4053e3b0295" +
+			"2267cd30589b0f5d993a5485a6ead1ffab9f2f4294c5853ba76383a326a6" +
+			"a42fb8b78948aa49f0f1f614bd0a3fbd2a58a3197daf2094605bd838285a" +
+			"1260f1265dca74aadd95652632335fd17cafcb73b202c3f0e5da836c2dcf" +
+			"2934f005935dca80154af43fa34c8ba440d1581b74ff17dfaca369dc9aa6" +
+			"734c03916d78e1b952691cef918fe033d33f7f4323cf724ffb8cd6c219bd" +
+			"046e9f268eb0601098e93daa59dde370e46269dd7c54891f71bee2829a53" +
+			"df86a2c7fb1046cd7c98fa21cd83597be554997a70acebe0b6e60f1f7098" +
+			"6f65adcae24385cb7102bdd3e01300ffd15d00f9764b3a5c51e35e5c9cdd" +
+			"da84f4b656fe514ec4ff8dcd774373f8a9103cf36abefe875f7084b9bbd9" +
+			"42e0c997ec2d860a4b622ff1a39a628582fd81f237d3d8f6843d26ac77cf" +
+			"bd48003e8e8c591ff813a9a897e3149ff0297ff476299d717e54d885cdd4" +
+			"4c3ba6ebf54bc7a1",
+	},
+	{
+		key: "b15578da1020f662ada0ad4f33a180d9f8ad4991b3720bc42a22b52625c7414a",
+		tag: "b0e4ad4a010afd6dd41ed82868cda555",
+		in: "6d2afb7a9154064341bdbb533f11990d4987e7c90fbfc0167c1e58d6efff" +
+			"6010f7ed569dac62ad37183b0d384519ebed0bf9c6e05a070b4858e6b846" +
+			"547ab5e45619c866f83cce83dcdab6a8a6c36b115ac832de1c6d433b94fa" +
+			"35803fa1a36f1ee114f8632402a027a74ac110394f32ec4006beb0057f09" +
+			"a94dada8bd0d1ca9a14b1f2efb8f526d79d6438bbbaac0ca1a43935627e5" +
+			"d129d52c06bf6413af07513bc579447eccc3a9406645c94dae59dab98d6a" +
+			"f92fa90fd4efaaa4bec466806ed401d2083cda587139ad7e9ee2adbb1dfe" +
+			"a88b59dd788b954a0f52c3854a3fffecb4bea83debbb2f5f8883e6415d3b" +
+			"ac1b872df1afe185468adc59364c173082f1dd6da9d348f5f5ba2d216243" +
+			"23de1f623eeec875bf31d12acec40dc0c1b9562826f3105cdad4c43cf45d" +
+			"829aa8b14012c47847aef7a2a6e3935fd972235f5d3a7ce4ad3582785393" +
+			"602e2e27329914021eff38ed2926c88acec1551f17a1b818fc1c3ed4b3b6" +
+			"6825d55bea269d710123b52e12ca9520a069d9c6a21df3a0253b3a4a6a8c" +
+			"dc226d667541548834da6bdbbdc165f39e40047d4b647c507d981be17b3a" +
+			"836063436241a8bb46b11a2867b621413c42d838e4578b72cc1982e34bde" +
+			"c303b5575ef4b8dd9fea8ed5bf69539413909d03461d3853b5fbf714a61c" +
+			"769569f42b38fac4b849104e2f2ac1dad0e388646278789f83e0b0511571" +
+			"019d3bfc5b03ca4cb5564e4e75e103ea1b6000be6588e27105d7cdc2d2f1" +
+			"f680ad34ef823ac4bd4068146e9997834665aec7dcc7a82ff28d85d52dd6" +
+			"9c18dd35f326bcf709f74df5981bb90ca8e765fef9f0698a19e12220b287" +
+			"24a6d9e4f4c7ce93f8ca9a126689ad1df820072557ce3db246cdf41599dd" +
+			"44ca841bece6c7869358005536e1189aa86b764e890ef90970d6e3831def" +
+			"fa890bf8692381123924e7d9df804fd770a0a30ee97d5dcdca302833efe8" +
+			"1d4b2505b17382f0b3429b38c41269ac95e36e9f5a1dbc6e6c8963741917" +
+			"02a23198decb4efe6809fcbeb5d0c9098a4c300155dc841610e55c8a6e27" +
+			"2a38a39de3d8ebf38a750af25836ffb1bb7822bb98886280f0cab6838c01" +
+			"cec57961bdc2e1bf158248309ff9294adcb962252b1c24646d132a3be2c9" +
+			"1ff82e8e101facbdb807826cc9d1840a90874ba08692e808c336c9d280ee" +
+			"f36a43a75c746fb864f85711e802546ab5cc3f8f117904ba1a85d6e4b729" +
+			"85122c5041891e16d55b93d6fc1b7fcfdc80ed3d72d55d64b8895bbf2f8e" +
+			"d188684e7e89afdc1e6a7ab9bd1d3da95d68698df2cdcbb2e1a4ae70e2fd" +
+			"dd4760f9e5cf4255eeb1e9e8009ab507395bacb8b2177e7c5757ad02baa9" +
+			"a96db967d20a150d2dd7f3081d90675fe0c82f94aa3cfdf6ac5585583901" +
+			"7a8e122170cc817f327a3c8ef44acd6e4fa81b73bcd0bcb5792eed470481" +
+			"152e87f7a20c3f7c69d5a8199bf9bb7c7269b450dc37a9b22102acaa8438" +
+			"134d6d733d231cee9522f7d02fbb37b5818ad3ca72df4752230ee11392ef" +
+			"8f8219be55202bc3d476f5a9078b32fb63d42bed4cda5ef90cc62467bf5e" +
+			"418ecd9d5d0cf1a33eb9a930e652ce96057fef40b65588aac67621d651a0" +
+			"9003dbc3925912e385296cd3b2b386a44113308ddf2af52ca390487eb20c" +
+			"716b76d78ad45129e7c285d918de7107ea8c3b0cfd9e73933b87c0b2b505" +
+			"cb4c95794f2ee6d6d43e2e76026923a0bbfbc3bb22df9ad729452283ce62" +
+			"dc9b26684fd45e07650581afd73713a708869a069c58b599ab478974f206" +
+			"dbd3e4e563e346ff1881723c5fd440bdf9f70f761c6f746113397d7c04b6" +
+			"b341d7e44de7de0aae79badaaef5ed372ef629dffd52926110683ab2d4da" +
+			"a4be83eb86c8700703a660edd5a5029f66f1581da96fe1feefc970ab4086" +
+			"a83ae02e959821967bd27b3b629652f5bc3db2b7f1af674f9f3fb3a788f7" +
+			"88e6dc1722382971831a7ed72502f85b25888c1534d81c0a4f7351ecc40f" +
+			"4e0412e05718403fae5746d313a78c80ac297f1391ad389070410e1330a1" +
+			"b07d683d1c795bda74bde947f2cf0dc9638b5d0851cda27df030403816dd" +
+			"3b70f042888c9c192656cc4b9fea10b81b5347900d9199c8f0f47d42f2ee" +
+			"482b68acfa5ff47d9950c950a926a497d94c6a796e0b715416520bd6c59f" +
+			"30217718d5f1d7bf7c24039f6467214ac8783cf011b25c37c67dfddde426" +
+			"40afe97f94879f4586954737b86701b32d560f08caec3fc45184bc719c7c" +
+			"5bf699074fde814acae32c189158c737665a8f94637068322f0c23ff8860" +
+			"f1b1c1bd766440afee290aa6f7150c7adefa6d72a738cd2268da7c94788e" +
+			"bb39002e9a328a51f3a92dc5c7cd9e4faed5702d3592ad16217c4978f84e" +
+			"af0fd2c9e4c6f4dcdd9112c781eb41a9aacb0f7935bb5c92d41e67cfff6b" +
+			"991ccefbd667ffeded1de325da50c33e28e2eef2f636c9726dc5bfe753ee" +
+			"c7bb6e1f080c89451f81bc8c29dc9067ce83deed02769714fa9bb477aca5" +
+			"c09089934674a0cc8e4b2c3136b2e4af8040cc601b90a4dec898dc922ca4" +
+			"976ab5ae4ac5af93fa5b1854a76ac3bcc2090bdeaa49ec4f319cf7c7b674" +
+			"6d8e617abb3361b28b27983dd1b139ec4f5af7e116439d7ecb16534817bf" +
+			"264dbd8f59e80b443be12c17fa013c7f4d029504c9bb62b296c2326f4f49" +
+			"cc3201b70ac3f62abb683c630179594a6d4cf30fd55b163bf8d01986bb6b" +
+			"cb7050fd527f095c45661920268e56f760fee80a29c9d37b7fc23f608710" +
+			"1e723038e64ee1b91c4849d69bd95fc9bc24fc4a234f4855f2a203e3f699" +
+			"c32698585c83781677739f2c48697c93b3388dcc64aa61f01118495ded33" +
+			"21ef9a1c949481f96005f8d5b277a7d6a0d906ec304cf4292df172e72d20" +
+			"29ecdeb65f06267a605f376804bf7bc5b82d5c8facfe7e41dc10806d27e0" +
+			"bcc5a341d80b3c1532407f75088716d732632cd88b0037f0d829bf385fec" +
+			"b52a202956489f61f16b0f4781bf59068b33d7330571d0b4a6ed91830258" +
+			"e1220b308784fa155be9bc821f5c0009a33802fa66dd66d1dde997dddd97" +
+			"873ddf65927dc1be979af2b5f110eee627dc1e210326ac20544a757ac168" +
+			"1823f3dd04b1ddc4bf96677a0a87633994e7af2ec99b7d5dfe44c6192be6" +
+			"a6e69d17b074256da3947808fbf68c7506a7e2c99e6b64d1ffadbd6285d8" +
+			"e7e032e24d42dde0594bf03fd550be05e5d66c91a660cd1ab7cb1f43fa9d" +
+			"69885203a7aee35a28f117427d7ac02b742f53d13b818f8631081b1730d1" +
+			"5b4e1e283cc8e5c4fc3b4652fce05fd8db821f99fcf93e6842816a549791" +
+			"7f6c49cc53d733788b2fe3c687de58bfe6153c70d99380df1fd566a7c758" +
+			"8052c62e73340d6a9eccd2ed26b763d518f3a0c4d6362212fbecebb4ffb7" +
+			"dc94d29944fcc4ab37725b105aa7571f364146782356d8ef056a0be93a55" +
+			"0c890df8fecc178776fe40703ad1bd2443d92c420be4306d99686592c030" +
+			"fd3e2230c0b48d8db79002e8a832ef27edb53a45532955f1171203d38414" +
+			"b4692e901e9f40f918528fc494430f86cf967452f456b01846ac6a383fc0" +
+			"de2243c7d804e8643aabcb78e2653b145f400a999670217c8da43bbb9c11" +
+			"e074176424be0c116c304a420120138e901eca4b12ce68fec460b23bc0c7" +
+			"765a74fc66cbda0e503e7b1baf5883744e468c97c5f1c4b0acc4b87de9f1" +
+			"4b537405dfb28195439d1ff848d9cd28a8d375038ebb540a9075b7b5074b" +
+			"ebc18418a370f1d3ac5d68f5d239513002ad11bfc2b7ff53e2e41ccffc4b" +
+			"0503acc4967c93ae8590a43439b5e7987d10cb8d1957bd9ef717ee3d12df" +
+			"5d6736c1d8bd8da102337a94b7d14f830f6c403cbaf7925a8a2a7af1311c" +
+			"57224967a38f6ca374013a9819c55fd2e2a5fac4f2490be5b059f4cd9c60" +
+			"2d62f80789eb8d9ab893c7f44a4945e41886af218179dfa754bbb59aab68" +
+			"13b71d2202eb8fc8a425625d21176a28a620e21bb0dad820c0b7051ce8d1" +
+			"3a33f3af0958bb6cd89f9d6414ab00ddd1d2f9fdece9183d0c05fcdfd117" +
+			"10d250e4b2029e6992a88293d0457e73e5b1b6a1aae182c69b9cb664992f" +
+			"073595ef68117026ad7ea579a4043cda318931eee7b2946a34cdc7c9755f" +
+			"80cc79a2bfe3ed9c79dc52faa5126b824868c965eeb37e9e4e6a49600f3a" +
+			"cce93c0853b546edb310dcd16a5755f15b1098b2f59dbd2d90e2ea8360ba" +
+			"f12108236e854465456598ae2f7bc380f008f2e3cd7c98c87643cafd7c36" +
+			"d40e2597236428d46aa5b260f84b4212d5e26804086adcf00363ce4becb4" +
+			"9b57eb2847b2f18ec82c99714ad4ddfe4ff3bcac1d0fcaa32660a1dccc68" +
+			"5bed83254c8e2ea0ae3632a70cfbcbeadef922d78a006d43ac7ab1f8a609" +
+			"c6e0ebc3ca6bb8430f1a562f41010db74b9febf931ca794fa08d1bc17780" +
+			"532ae76f25c4ee679d788835dfa4e70ca154c9e2865c3750ffe7b837eed1" +
+			"972be058fdf2bdb3eb301867bb132306c7aa237f6771d60bbc56cf31cb30" +
+			"32a87204d454542de747418470025ab84935d3eaaca01dbbdae9ef6b5d3a" +
+			"ca62ce9f871a3e1272b2b671582c096a349c00f32d742ddb17993994d8ae" +
+			"fc178cbcf9abc03114ff2bf7db8f757c63d6898faccd822f5c2e9a7570fb" +
+			"9cfff148570888be24ae42644c1a5bebb6f6287147a4bcc01c7675be9e4a" +
+			"897519dd3132a7cc2e778f8c90d23dc8073f6fa108d7ef82d561794bd9d5" +
+			"f1faa306334f338ac3ba99c853f79c24f7048fa906fde87d1ed28a7b11c0" +
+			"66a3bb98f8d21055aaafdf7e069b77b60b3d5cbe7c5e4379c7651af955cd" +
+			"82a19a09caf36becb6cd3fe9e12f40379941542709991066df21b7b12dfb" +
+			"2416d83fcdc33bb583e3b42f24f53edf8dc7c579ad3be831c99f72bf9fb7" +
+			"a35b6562e824e039e6bf1adc8f5ca53846de7bae11c4317e696d887df33c" +
+			"525f0a9c01fc29f2c26c90b85fe82ed8bd50954cd4e9ac7c85c7f3efec75" +
+			"da1da4ed173cb695cee295190527edb3cb06c5dbdabe0228cc60b6455153" +
+			"76244f27aa56da2db10f2659090137ffb82c57233c833e0bbf22d6f647fb" +
+			"97b3652d2888b3ab08010b8e8a6967d560b747757806736dc98b78226634" +
+			"f1eecaa4a2e23ba36591acb5737d735c5bc7a2e36f1a46946927e061fdf7" +
+			"7a3b68ef582c26b01f5aa9a438ecc26c6941221d1590c838072f9e471fe7" +
+			"fd59dacb0d092d40d76ea2f7c6e954a132a015bd4cb31147f3ebe4518322" +
+			"916438a62836ac85a4cf4492190a85bcc8edb37e38b99ea552d749c30f74" +
+			"ca20c298165e8ed02d4671e0b41cac3a32a345b9349ad22c2a4bb2c16a4c" +
+			"e0613ca0f0518759f7d2b33cfad2fae764f410d4d9ff8a76ae02a8107e7e" +
+			"01d9cd0552676b85ba002f19c01ad5f416d1d08bb84fec7c3555b098dbce" +
+			"48e1a5d847895e54db9c5b80cc22d5b87cd41a1a94be102bdd45a3cda5d1" +
+			"181e10446d213d6b3fdc350d486d2011d705c5f16ccf7519065c47bad7d6" +
+			"89c71e5fdf9d04bfb91eb1f07fa0f001009c1d4b1f6a116a570823a8580b",
+	},
+	{
+		key: "392468efccff36dade31fc1c62eb38bb61394fe448def9d9d9beec2413ddb418",
+		tag: "e1122e7c8e6965b90addbd46d8a548d6",
+		in: "6a13d37f0ec933194c227351f4a19b507d93465b1f3e88dcb5f1ed1262fa" +
+			"58ea99ff31e6fc85c39c04129fa69195b71b2060122fe618dd9430a63f97" +
+			"54b52a80b3cd099f248f91a468bae211a27bdb47ba005d29881ea5143a82" +
+			"967c4c30c9a4f0dba1a4975e6407fe296d40023a00efa06be763f2d73d46" +
+			"a2901ae28b3d8ce18009a462e223b71476d7b954c138e177d15a390847de" +
+			"96a7f7fd0598748e86b0f08e64d915e67c7e3cf936f3dcd60edebd36e2a1" +
+			"d65b6ac29530c48ab3bd52d45b4f938a19b9b31e2911105a8561600d5377" +
+			"905a67112ec28025aa680350ff85b808c5b4c98b7b9567d03f5ed3911ec9" +
+			"365a8de4b15ca62adaa69e5ba710eb1756a346016c67a297d8624f9f1ab5" +
+			"b3fbce98b141049f0ce26c85d2f8a9cc6ca8ab6c6e148be968931430dcc6" +
+			"2bf58ea9698ef52a5d271cf48e6748ac9e04bc7ae7da205a1a7535478322" +
+			"d820eca146cedf4b2f9aa9fcfd77ab56a7276977401dcc1f96baa1b607e0" +
+			"256bd04ec324ec67a4313e2d5a53d3a3fb5332927929b20c63bde805f637" +
+			"eb1050fee2a152a0405634f55c48a59fe370d54b2ab1671dae2c7fd92243" +
+			"10627808e553127c74f724362b4a6ee49b697daae7df3ddc5d2ed9d6befd" +
+			"77fb9f68fe3041f6ef13f46f34ab682ab8563e8996344f82b2ef006a8d54" +
+			"3dd9c1db4979d7da97bda45e722065f8a238f0873217b783a9a629a12b3a" +
+			"4de437445039997bd243efbf5e3b6059b9459d395290efb9081c632fb694" +
+			"81000dc74c395cb507422df181aba20f776ce3fd8765ac485021992c98b1" +
+			"67c68805662cb4356a0ee7ba6bdae51ac10cd06bb5b2f3a72841c714c8ed" +
+			"bc56998fe2fefb9bf69e172fdf54b2ab138ae59372c52a67e93882a3000f" +
+			"d966992aa2250c6ff93e9cac89645d70625d79332ade5dab7eb1adbe7dce" +
+			"5a013fb65ad32fe22ed16fb9bb35eca1f37a0433c320e8752f8fc4b7618c" +
+			"5e4df2efece832e259ad98b895c474e47d0e3fc488bea8f717a17de0dcf7" +
+			"597fb8fe12e62246296f9a887dcc3a700820c190a55a4931a7d44bd3bb2e" +
+			"ab6c8a8126f1be93790cebabc1d69e01796e6cc80e7c16bbc82fb333fb21" +
+			"c774ab7db843242838e82d8e1cb8ccab385e67a4271fe7031d74b6e8edcc" +
+			"8ed585d1c05a365c7665899c1dbc561151d3b44bceace77c4f53c0e0f6f7" +
+			"74d42f9ad3e56f1c2a8d53879d695f895690afb4698472a3d52d67159313" +
+			"133c87823fe0500eb68fe286f8b9a2f59f12785d026dc97bdbf793c7d1eb" +
+			"155f1f136aae66c256583e987f718afbe733e0a5ce30d021493fb84e2242" +
+			"5b18754d126235ef80335004fa84f88361a584753df409360cd8bd45bace" +
+			"8f48156bec66577bf2c685089f5ac7e7ec76c0df068fbaa47661f8517f92" +
+			"e14723b3b278f151816537a7212c96bd340a00c15c9c9bc9a2a5d163655d" +
+			"84b38073e2be9217cad97d362d89d4baf3ce0a8d8562f19a8c97a9aaf5e7" +
+			"77d60456360ffb77b30f177d2809052020d141697ecf9cb65f42b9190caf" +
+			"6540b2c82f6e5a8482934a6a1a5711a8c24546cd8ba432068404eae5a827" +
+			"2e09efc3c6037af4feaac0a46329229b010ecac6b9f077a9b076bb6d9ce1" +
+			"38401eb38d124baa11507a994185295020bf9b754fcf78430db9253f5929" +
+			"87c46c0f8589c4e463b15a3840b1cea795e24cf6b20f29a630136e0589b3" +
+			"8dd7fbe5ea21da72c88bd8e56473586822aa3765660a45a988df9b8eb8e8" +
+			"141939d3e4cc637c5d788064d40a9f7c734e43fdf8d7189a5d76700d9743" +
+			"fe0122944663afdb88c5201318ca782f6848b742ddebe7463fd4a32280ac" +
+			"1cf8311e9137d319de05ce9cd85abab24c5364041c14d3b4ce650400498e" +
+			"122166eccc12784b7ac3b262ac0b198ffc26eeed9a5da5374f7a2a53c87a" +
+			"78c217ea1fbf8d38f62511657b73109f31691aef14d82ce6e1010eae9e6f" +
+			"a419e5c1c16c0cc70651eb3374c03549a1bc7d3ed42d60f886102c798dbc" +
+			"ba56f0a2b3b9b412530c35f5f7ed06311ee14571f9c26ed9c81ef38ff000" +
+			"2f5ef3aab7351e32049a6ef8f48a43da1d84402d229df513dfaf1b2e4043" +
+			"6ce68c70ebeddd7477c9164f0dce45a6fc5de050f52ec269659d5854bcae" +
+			"f7762ed7400713c27a4d523eaf8c136c4a1ca00b9e9e55902daf6cdf8528" +
+			"c22ca1f2fa7ce87902d75a6850e1a5a4592497be1bb401878f18b189b0e2" +
+			"c59d10705bfabde3cd2da01eb452006b294108d5d42e88e9e15424d8cd0b" +
+			"8ab43a6c546b3dbf52e47b59cde6a3e417b0395220b6d63736d429da3458" +
+			"9a2524f1629320206fa7f1d8a041e17222c4a5814561937e1030e6375c77" +
+			"9dc988bb928bbdbe2c2eb20111639725d82b5d7192cd3e4acc27581f0ba7" +
+			"286cff41f97aa5a52ea0083de5057fd2ba985aa738e4d03fcf11ebab1d97" +
+			"e2ac77d1c2beb8799150a421a07b3777d0b850f24194b8309135b13da6c7" +
+			"e38653a711e407a1811290fbb7bc15d8b12efc6916e97ead41e042a44721" +
+			"e9cde3388073d921595bcddcac758dc675173f38242e65e4a284aaa7e8fa" +
+			"6adddaf00bc46428ab2d8601205b8895bcedfc80ca0aa4619ed6bb082ddf" +
+			"33ec04fa5d417f33fcdd238c6b11320c5a08f800e0f350b75d81e3bcbd15" +
+			"58a1eab87a3c8c2ffd7ba1d7e754e607cf98ba22a3fc766c45bd6f2569b4" +
+			"84639e6611714119d188a24a5e963089a16ed34e20b9f154cad8ac6031dd" +
+			"7a3a885afc2ae5e003ae8d4e4aabdb3e51dfc423b8cf4ed9ae2010072cbb" +
+			"b1108c7da1ff075e54ed827a0963ac5523ecdf3fc5eee7b4d1a6773764ec" +
+			"5c30f41690523fd70d895edb7ca6a1806d54240c4c7b43410da73503a323" +
+			"90d9070ed30da3a2fb5eccd40d083be7cf8bf40b4279f819cf795b6f075b" +
+			"5a67a10a06a6076d0d83c72efea05f244901c4b5fd9eb380432519311baf" +
+			"8c81f6325df4d37ff4d30d318f904ebb837ec76b341dd00a8f247cf0bbe9" +
+			"6f3784dc8f5feb344958fdf1a9ececb105f8770826db1f17a5281e997951" +
+			"d3c60cc28fc3e66ffeb5dbac315f98f6d240208043f28dee963d843e68ab" +
+			"57d847f76ae2f96ce6e37f377ef5dfef2176ecd7440ce4dadcec2231b606" +
+			"e4a80420fb3ed135640e1f05d6bd58b8dce062dd7d36b885d424f6318e5e" +
+			"a0753efbb33bbc7360d2b5dfab3ae0d5e000b8d31f2ba0f5fd8b34f96b55" +
+			"28fff35e769461d0f03cf3bfdf0b801dcbbf2838180cb9b108e06c353e3f" +
+			"0b9ef61678cfed1ea37ae76bccb5ef5957ac2c8e8f4794c8145a15f1cc88" +
+			"bfb0881080326c481b373c3bc9b07a9b60a0c8bd5fa4f6f90145590a5227" +
+			"6fcc0ccc2375d0ccb571d414d1b0c38b4e02c39db4d701c5e25e90785ef4" +
+			"d26f35edd8c4b96455bdca7245cfefd9cfbd2f319615e5fdf07bb9564fa0" +
+			"44bb35a58391d02e3927780b4076bc0893dfcb4b63a32cd7a541a4a8c253" +
+			"0349c6e96e378dbeb66dedf87d813d0b744452c1c4088507dca722193827" +
+			"9e2dfa24e4a409de494acf654f44262db9206a7717fa434ac4fdc6a6eb5b" +
+			"1fd5a193b6043bc4327c8c09fd6822eaa9df37bbcac1077754a295621601" +
+			"267b68733b62dadc2563f1700af180141f29899e2689dbbe9745ba8477f4" +
+			"352921900b403a01c9dd042a8c1b0e0489959fb0b0a8431c97b41e202204" +
+			"212ebfa00c593399dbd14d7aec07b8292d2e40b48f05fcd54a15da4a24d7" +
+			"2759e409f4c7b5b98fce4abac6c30e4872d92efa1f96479ec30f21699825" +
+			"50fa60584f5a09051a00f8e7dbb3853e66ca3f05fbfe43bef9b120a25a01" +
+			"eb436ba8ecda715201eda72e517d628f883386c1503aa8b8e75610f7155e" +
+			"9f916335ab6d6f0f9589b6220cd2b81c2c937dc065d3d14a7df8cc916cd0" +
+			"0ce1bb53fd9c8974298d3bd316f3658aa8cc6904f073a1472149e4b08c64" +
+			"5e11abe0428ccb6174df2103edd735965d6454b543d3f01410f77053f65e" +
+			"c1d1aee56fdd3af23bcd4e1a7fcc4e600c4831007c33fe5f0c8300f686eb" +
+			"9b4d1e4f08fe4ddc8a90be14dc3a5a88ff96716509341d5db24c0d016863" +
+			"998b1859c5021df815a6f1ca9845f1a8e99dbad132b406227c5897a1bdf3" +
+			"e698962f799133ff4429decbef6ce036296facf38e4812fec102b76c6d30" +
+			"beba1b70722254fafbc471096153478c971db7d96263660209265cb10f13" +
+			"b34b5fd55c4abe818a5f9715d8a85094e2946b7a001b47f629e26c636d86" +
+			"4968ad2ab616dfe28840bd60b4b9855c8dbe1cb873fcbc4577b5fefeb8bb" +
+			"4832039867dc35db9c036c83bc204396e3474ddfe806c77c65c936f488b6" +
+			"7c1028739562d7bb055d21441af29ae2921290e548dccf8a56021385422b" +
+			"15da6b232b24151309a75a00296d11aa1952a1513110b0faa93d1d8cd9ae" +
+			"fa9f1c59377ec9165b2c9e07cbde40db7b81bca6d58fc28bae8f473cd0e9" +
+			"a2420e0b943a83d284108626c24ac570b1d6c1ab971e71f43fbd6c00e171" +
+			"238141a6dc987a60385c3a04dd147a2f8e80dfe727b104c0fdd80b326f59" +
+			"0b9f86fd7b2fd1122a390979889eabd803ab57159c8509a1443eb6789382" +
+			"090a770ae4eba03306f96e50e19a7d44c584ccc230d104548946efca4520" +
+			"d61de5f473e2f4eada6c8ce9c7ee975eb4f63c0483cb775ed7d3cf690a61" +
+			"7d6656d683a8512707d81ca5ba176a42bcffcfa692129f292607d2a47536" +
+			"ccaeb464c9272d6f3816074b712af602470088b253deba18771e5f67734b" +
+			"587707cdd06f35264b2262fd253c25b5d38ee7db287610e5398062b7a34e" +
+			"6e4cf7447d00873b930ad148fd96f0ab18771bc468b874bb109924101c84" +
+			"c4e239ecc7687d875e4d94a1a973620ca61e35a872c2e2e61a502169f1bb" +
+			"4e5ff5fa2bff657be6195b3e2c7151a52fc0096d98e7f08f5a98f570aee1" +
+			"7b4275f1356e87e080ce0e1b9bbabe7dea48b5903bc390ce23472ad64a89" +
+			"41c3247bfd23ea90b2dee09085571bad85568040105e098f993bb37e43c3" +
+			"e6d511171c77cfc450570dfb9fc6a3930ef43c03f8213f6203d545d791c7" +
+			"d3fa42d5dde1655038d35c5dfacc12e9dee24fe833977549eda68ae8b508" +
+			"be277e743921b584f9dfa0eefbd8bf3c23f51efdef7f7487001d29e8097b" +
+			"ba63289cfca743023d1668555a46fe6d5b7421377414df1e9ef135480622" +
+			"22e2e9a7baa618d88f407517f6317b6a0ba3384ace16d68631d59ea169d5" +
+			"092d20afc1a481b82be5e734bb092953a0a94702bae1a0f48d2a22b9a05f" +
+			"f64493b7b2e984f27582b1eb937fddf8512c49830435d146dcc291a4118d" +
+			"5dc638b99cdcbcc5860de7a92c5b13cbd1e01e051f01af40afe124346320" +
+			"d3626bf9d8f7850744e032a993c276fd388718237740c6caf260fca60b8d" +
+			"d846102e3262b6e05ceca00c6affe938fac1847350865fc858d3ddd1d130" +
+			"71d1221ce7c5d575587fcba580e544b74d877ed5ca92763ef0ca0d7bfa08" +
+			"d57a0216b2a01a2b9ec74b8430051e0074862b7be25b6766ab520f2eb75d" +
+			"eeb979c28f03795f6f1e4b8410beab19a20febc91985b8a7c298534a6598" +
+			"f2c5b0dc5de9f5e55a97791507bc6373db26",
+	},
+}
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 6bbaecf..9631e6e 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -5891,59 +5891,65 @@
 	})
 
 	// Test TLS 1.3's downgrade signal.
-	testCases = append(testCases, testCase{
-		name: "Downgrade-TLS12-Client",
-		config: Config{
-			Bugs: ProtocolBugs{
-				NegotiateVersion: VersionTLS12,
-			},
-		},
-		tls13Variant:       TLS13RFC,
-		expectedVersion:    VersionTLS12,
-		shouldFail:         true,
-		expectedError:      ":TLS13_DOWNGRADE:",
-		expectedLocalError: "remote error: illegal parameter",
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "Downgrade-TLS12-Server",
-		config: Config{
-			Bugs: ProtocolBugs{
-				SendSupportedVersions: []uint16{VersionTLS12},
-			},
-		},
-		tls13Variant:       TLS13RFC,
-		expectedVersion:    VersionTLS12,
-		shouldFail:         true,
-		expectedLocalError: "tls: downgrade from TLS 1.3 detected",
-	})
+	var downgradeTests = []struct {
+		name            string
+		version         uint16
+		clientShimError string
+	}{
+		{"TLS12", VersionTLS12, "tls: downgrade from TLS 1.3 detected"},
+		{"TLS11", VersionTLS11, "tls: downgrade from TLS 1.2 detected"},
+		// TLS 1.0 does not have a dedicated value.
+		{"TLS10", VersionTLS10, "tls: downgrade from TLS 1.2 detected"},
+	}
 
-	testCases = append(testCases, testCase{
-		name: "Downgrade-TLS11-Client",
-		config: Config{
-			Bugs: ProtocolBugs{
-				NegotiateVersion: VersionTLS11,
+	for _, test := range downgradeTests {
+		// The client should enforce the downgrade sentinel.
+		testCases = append(testCases, testCase{
+			name: "Downgrade-" + test.name + "-Client",
+			config: Config{
+				Bugs: ProtocolBugs{
+					NegotiateVersion: test.version,
+				},
 			},
-		},
-		tls13Variant:       TLS13RFC,
-		expectedVersion:    VersionTLS11,
-		shouldFail:         true,
-		expectedError:      ":TLS13_DOWNGRADE:",
-		expectedLocalError: "remote error: illegal parameter",
-	})
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		name:     "Downgrade-TLS11-Server",
-		config: Config{
-			Bugs: ProtocolBugs{
-				SendSupportedVersions: []uint16{VersionTLS11},
+			tls13Variant:       TLS13RFC,
+			expectedVersion:    test.version,
+			shouldFail:         true,
+			expectedError:      ":TLS13_DOWNGRADE:",
+			expectedLocalError: "remote error: illegal parameter",
+		})
+
+		// The client should ignore the downgrade sentinel if
+		// configured.
+		testCases = append(testCases, testCase{
+			name: "Downgrade-" + test.name + "-Client-Ignore",
+			config: Config{
+				Bugs: ProtocolBugs{
+					NegotiateVersion: test.version,
+				},
 			},
-		},
-		tls13Variant:       TLS13RFC,
-		expectedVersion:    VersionTLS11,
-		shouldFail:         true,
-		expectedLocalError: "tls: downgrade from TLS 1.2 detected",
-	})
+			tls13Variant:    TLS13RFC,
+			expectedVersion: test.version,
+			flags: []string{
+				"-ignore-tls13-downgrade",
+				"-expect-tls13-downgrade",
+			},
+		})
+
+		// The server should emit the downgrade signal.
+		testCases = append(testCases, testCase{
+			testType: serverTest,
+			name:     "Downgrade-" + test.name + "-Server",
+			config: Config{
+				Bugs: ProtocolBugs{
+					SendSupportedVersions: []uint16{test.version},
+				},
+			},
+			tls13Variant:       TLS13RFC,
+			expectedVersion:    test.version,
+			shouldFail:         true,
+			expectedLocalError: test.clientShimError,
+		})
+	}
 
 	// Test that the draft TLS 1.3 variants don't trigger the downgrade logic.
 	testCases = append(testCases, testCase{
@@ -7625,17 +7631,6 @@
 						},
 					})
 				} else {
-					error := ":OLD_SESSION_VERSION_NOT_RETURNED:"
-					// Clients offering TLS 1.3 will send a fake session ID
-					// unrelated to the session being offer. This session ID is
-					// invalid for the server to echo, so the handshake fails at
-					// a different point. It's not syntactically possible for a
-					// server to convince our client that it's accepted a TLS
-					// 1.3 session at an older version.
-					if resumeVers.version < VersionTLS13 && sessionVers.version >= VersionTLS13 {
-						error = ":SERVER_ECHOED_INVALID_SESSION_ID:"
-					}
-
 					testCases = append(testCases, testCase{
 						protocol:      protocol,
 						name:          "Resume-Client-Mismatch" + suffix,
@@ -7654,7 +7649,7 @@
 						},
 						expectedResumeVersion: resumeVers.version,
 						shouldFail:            true,
-						expectedError:         error,
+						expectedError:         ":OLD_SESSION_VERSION_NOT_RETURNED:",
 						flags: []string{
 							"-on-initial-tls13-variant", strconv.Itoa(sessionVers.tls13Variant),
 							"-on-resume-tls13-variant", strconv.Itoa(resumeVers.tls13Variant),
diff --git a/src/ssl/test/runner/sign.go b/src/ssl/test/runner/sign.go
index ceae05c..0c963d9 100644
--- a/src/ssl/test/runner/sign.go
+++ b/src/ssl/test/runner/sign.go
@@ -18,7 +18,7 @@
 	"fmt"
 	"math/big"
 
-	"./ed25519"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/ed25519"
 )
 
 type signer interface {
diff --git a/src/ssl/test/runner/tls.go b/src/ssl/test/runner/tls.go
index 225794b..128d22e 100644
--- a/src/ssl/test/runner/tls.go
+++ b/src/ssl/test/runner/tls.go
@@ -18,7 +18,7 @@
 	"strings"
 	"time"
 
-	"./ed25519"
+	"boringssl.googlesource.com/boringssl/ssl/test/runner/ed25519"
 )
 
 // Server returns a new TLS server side connection
@@ -311,8 +311,7 @@
 
 	if bytes.HasPrefix(der, ed25519PKCS8Prefix) && len(der) == len(ed25519PKCS8Prefix)+32 {
 		seed := der[len(ed25519PKCS8Prefix):]
-		_, key := ed25519.NewKeyPairFromSeed(seed)
-		return key, nil
+		return ed25519.NewKeyFromSeed(seed), nil
 	}
 
 	return nil, errors.New("crypto/tls: failed to parse private key")
diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc
index a1793da..a02d35d 100644
--- a/src/ssl/tls13_both.cc
+++ b/src/ssl/tls13_both.cc
@@ -30,7 +30,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // kMaxKeyUpdates is the number of consecutive KeyUpdates that will be
 // processed. Without this limit an attacker could force unbounded processing
@@ -101,8 +101,8 @@
   return true;
 }
 
-int tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
-                              int allow_anonymous) {
+bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg,
+                               bool allow_anonymous) {
   SSL *const ssl = hs->ssl;
   CBS body = msg.body;
   bssl::UniquePtr<CRYPTO_BUFFER> decompressed;
@@ -118,7 +118,7 @@
         CBS_len(&body) != 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      return 0;
+      return false;
     }
 
     if (uncompressed_len > ssl->max_cert_list) {
@@ -126,7 +126,7 @@
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNCOMPRESSED_CERT_TOO_LARGE);
       ERR_add_error_dataf("requested=%u",
                           static_cast<unsigned>(uncompressed_len));
-      return 0;
+      return false;
     }
 
     ssl_cert_decompression_func_t decompress = nullptr;
@@ -141,7 +141,7 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERT_COMPRESSION_ALG);
       ERR_add_error_dataf("alg=%d", static_cast<int>(alg_id));
-      return 0;
+      return false;
     }
 
     CRYPTO_BUFFER *decompressed_ptr = nullptr;
@@ -150,7 +150,7 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_DECOMPRESSION_FAILED);
       ERR_add_error_dataf("alg=%d", static_cast<int>(alg_id));
-      return 0;
+      return false;
     }
     decompressed.reset(decompressed_ptr);
 
@@ -161,7 +161,7 @@
           "alg=%d got=%u expected=%u", static_cast<int>(alg_id),
           static_cast<unsigned>(CRYPTO_BUFFER_len(decompressed_ptr)),
           static_cast<unsigned>(uncompressed_len));
-      return 0;
+      return false;
     }
 
     CBS_init(&body, CRYPTO_BUFFER_data(decompressed_ptr),
@@ -177,14 +177,14 @@
       CBS_len(&body) != 0) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    return 0;
+    return false;
   }
 
   UniquePtr<STACK_OF(CRYPTO_BUFFER)> certs(sk_CRYPTO_BUFFER_new_null());
   if (!certs) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
+    return false;
   }
 
   const bool retain_sha256 =
@@ -197,7 +197,7 @@
         CBS_len(&certificate) == 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH);
-      return 0;
+      return false;
     }
 
     if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) {
@@ -205,13 +205,13 @@
       if (!pkey) {
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-        return 0;
+        return false;
       }
       // TLS 1.3 always uses certificate keys for signing thus the correct
       // keyUsage is enforced.
       if (!ssl_cert_check_digital_signature_key_usage(&certificate)) {
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-        return 0;
+        return false;
       }
 
       if (retain_sha256) {
@@ -227,7 +227,7 @@
         !PushToStack(certs.get(), std::move(buf))) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      return 0;
+      return false;
     }
 
     // Parse out the extensions.
@@ -243,7 +243,7 @@
                               OPENSSL_ARRAY_SIZE(ext_types),
                               0 /* reject unknown */)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
-      return 0;
+      return false;
     }
 
     // All Certificate extensions are parsed, but only the leaf extensions are
@@ -252,7 +252,7 @@
       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 0;
+        return false;
       }
 
       uint8_t status_type;
@@ -263,7 +263,7 @@
           CBS_len(&ocsp_response) == 0 ||
           CBS_len(&status_request) != 0) {
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-        return 0;
+        return false;
       }
 
       if (sk_CRYPTO_BUFFER_num(certs.get()) == 1) {
@@ -271,7 +271,7 @@
             CRYPTO_BUFFER_new_from_CBS(&ocsp_response, ssl->ctx->pool));
         if (hs->new_session->ocsp_response == nullptr) {
           ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-          return 0;
+          return false;
         }
       }
     }
@@ -280,13 +280,13 @@
       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 0;
+        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 0;
+        return false;
       }
 
       if (sk_CRYPTO_BUFFER_num(certs.get()) == 1) {
@@ -294,7 +294,7 @@
             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 0;
+          return false;
         }
       }
     }
@@ -312,14 +312,14 @@
   if (!ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return 0;
+    return false;
   }
 
   if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) {
     if (!allow_anonymous) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED);
-      return 0;
+      return false;
     }
 
     // OpenSSL returns X509_V_OK when no certificates are requested. This is
@@ -327,18 +327,18 @@
     hs->new_session->verify_result = X509_V_OK;
 
     // No certificate, so nothing more to do.
-    return 1;
+    return true;
   }
 
   hs->new_session->peer_sha256_valid = retain_sha256;
-  return 1;
+  return true;
 }
 
-int tls13_process_certificate_verify(SSL_HANDSHAKE *hs, const SSLMessage &msg) {
+bool tls13_process_certificate_verify(SSL_HANDSHAKE *hs, const SSLMessage &msg) {
   SSL *const ssl = hs->ssl;
   if (hs->peer_pubkey == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   CBS body = msg.body, signature;
@@ -348,13 +348,13 @@
       CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return 0;
+    return false;
   }
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!tls12_check_peer_sigalg(ssl, &alert, signature_algorithm)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
-    return 0;
+    return false;
   }
   hs->new_session->peer_signature_algorithm = signature_algorithm;
 
@@ -363,7 +363,7 @@
           hs, &input,
           ssl->server ? ssl_cert_verify_client : ssl_cert_verify_server)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   bool sig_ok = ssl_public_key_verify(ssl, signature, signature_algorithm,
@@ -375,14 +375,14 @@
   if (!sig_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-int tls13_process_finished(SSL_HANDSHAKE *hs, const SSLMessage &msg,
-                           int use_saved_value) {
+bool tls13_process_finished(SSL_HANDSHAKE *hs, const SSLMessage &msg,
+                            bool use_saved_value) {
   SSL *const ssl = hs->ssl;
   uint8_t verify_data_buf[EVP_MAX_MD_SIZE];
   const uint8_t *verify_data;
@@ -394,25 +394,25 @@
   } else {
     if (!tls13_finished_mac(hs, verify_data_buf, &verify_data_len,
                             !ssl->server)) {
-      return 0;
+      return false;
     }
     verify_data = verify_data_buf;
   }
 
-  int finished_ok = CBS_mem_equal(&msg.body, verify_data, verify_data_len);
+  bool finished_ok = CBS_mem_equal(&msg.body, verify_data, verify_data_len);
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-  finished_ok = 1;
+  finished_ok = true;
 #endif
   if (!finished_ok) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-int tls13_add_certificate(SSL_HANDSHAKE *hs) {
+bool tls13_add_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   CERT *const cert = hs->config->cert.get();
 
@@ -435,7 +435,7 @@
       !CBB_add_u8(body, 0) ||
       !CBB_add_u24_length_prefixed(body, &certificate_list)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   if (!ssl_has_certificate(hs->config)) {
@@ -449,7 +449,7 @@
                      CRYPTO_BUFFER_len(leaf_buf)) ||
       !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   if (hs->scts_requested && cert->signed_cert_timestamp_list != nullptr) {
@@ -462,7 +462,7 @@
             CRYPTO_BUFFER_len(cert->signed_cert_timestamp_list.get())) ||
         !CBB_flush(&extensions)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
+      return false;
     }
   }
 
@@ -477,7 +477,7 @@
                        CRYPTO_BUFFER_len(cert->ocsp_response.get())) ||
         !CBB_flush(&extensions)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
+      return false;
     }
   }
 
@@ -489,7 +489,7 @@
                        CRYPTO_BUFFER_len(cert_buf)) ||
         !CBB_add_u16(&certificate_list, 0 /* no extensions */)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
+      return false;
     }
   }
 
@@ -500,7 +500,7 @@
   Array<uint8_t> msg;
   if (!CBBFinishArray(cbb.get(), &msg)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   const CertCompressionAlg *alg = nullptr;
@@ -513,7 +513,7 @@
 
   if (alg == nullptr || alg->compress == nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   CBB compressed;
@@ -526,10 +526,10 @@
       !alg->compress(ssl, &compressed, msg.data(), msg.size()) ||
       !ssl_add_message_cbb(ssl, cbb.get())) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
 enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs) {
@@ -582,7 +582,7 @@
   return ssl_private_key_success;
 }
 
-int tls13_add_finished(SSL_HANDSHAKE *hs) {
+bool tls13_add_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   size_t verify_data_len;
   uint8_t verify_data[EVP_MAX_MD_SIZE];
@@ -590,7 +590,7 @@
   if (!tls13_finished_mac(hs, verify_data, &verify_data_len, ssl->server)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
-    return 0;
+    return false;
   }
 
   ScopedCBB cbb;
@@ -598,13 +598,13 @@
   if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_FINISHED) ||
       !CBB_add_bytes(&body, verify_data, verify_data_len) ||
       !ssl_add_message_cbb(ssl, cbb.get())) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-static int tls13_receive_key_update(SSL *ssl, const SSLMessage &msg) {
+static bool tls13_receive_key_update(SSL *ssl, const SSLMessage &msg) {
   CBS body = msg.body;
   uint8_t key_update_request;
   if (!CBS_get_u8(&body, &key_update_request) ||
@@ -613,11 +613,11 @@
        key_update_request != SSL_KEY_UPDATE_REQUESTED)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return 0;
+    return false;
   }
 
   if (!tls13_rotate_traffic_key(ssl, evp_aead_open)) {
-    return 0;
+    return false;
   }
 
   // Acknowledge the KeyUpdate
@@ -630,7 +630,7 @@
         !CBB_add_u8(&body_cbb, SSL_KEY_UPDATE_NOT_REQUESTED) ||
         !ssl_add_message_cbb(ssl, cbb.get()) ||
         !tls13_rotate_traffic_key(ssl, evp_aead_seal)) {
-      return 0;
+      return false;
     }
 
     // Suppress KeyUpdate acknowledgments until this change is written to the
@@ -639,16 +639,16 @@
     ssl->s3->key_update_pending = true;
   }
 
-  return 1;
+  return true;
 }
 
-int tls13_post_handshake(SSL *ssl, const SSLMessage &msg) {
+bool tls13_post_handshake(SSL *ssl, const SSLMessage &msg) {
   if (msg.type == SSL3_MT_KEY_UPDATE) {
     ssl->s3->key_update_count++;
     if (ssl->s3->key_update_count > kMaxKeyUpdates) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_KEY_UPDATES);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-      return 0;
+      return false;
     }
 
     return tls13_receive_key_update(ssl, msg);
@@ -662,7 +662,7 @@
 
   ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
   OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-  return 0;
+  return false;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/tls13_client.cc b/src/ssl/tls13_client.cc
index cf20403..26f5fb9 100644
--- a/src/ssl/tls13_client.cc
+++ b/src/ssl/tls13_client.cc
@@ -24,13 +24,14 @@
 #include <openssl/digest.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
+#include <openssl/sha.h>
 #include <openssl/stack.h>
 
 #include "../crypto/internal.h"
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 enum client_hs_state_t {
   state_read_hello_retry_request = 0,
@@ -552,7 +553,7 @@
     return ssl_hs_error;
   }
 
-  if (!tls13_process_certificate(hs, msg, 0 /* certificate required */) ||
+  if (!tls13_process_certificate(hs, msg, false /* certificate required */) ||
       !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
@@ -612,7 +613,7 @@
     return ssl_hs_read_message;
   }
   if (!ssl_check_message_type(ssl, msg, SSL3_MT_FINISHED) ||
-      !tls13_process_finished(hs, msg, 0 /* don't use saved value */) ||
+      !tls13_process_finished(hs, msg, false /* don't use saved value */) ||
       !ssl_hash_message(hs, msg) ||
       // Update the secret to the master secret and derive traffic keys.
       !tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
@@ -846,18 +847,18 @@
   return "TLS 1.3 client unknown";
 }
 
-int tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg) {
+bool tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg) {
   if (ssl->s3->write_shutdown != ssl_shutdown_none) {
     // Ignore tickets on shutdown. Callers tend to indiscriminately call
     // |SSL_shutdown| before destroying an |SSL|, at which point calling the new
     // session callback may be confusing.
-    return 1;
+    return true;
   }
 
   UniquePtr<SSL_SESSION> session = SSL_SESSION_dup(
       ssl->s3->established_session.get(), SSL_SESSION_INCLUDE_NONAUTH);
   if (!session) {
-    return 0;
+    return false;
   }
 
   ssl_session_rebase_time(ssl, session.get());
@@ -873,7 +874,7 @@
       CBS_len(&body) != 0) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    return 0;
+    return false;
   }
 
   // Cap the renewable lifetime by the server advertised value. This avoids
@@ -883,7 +884,7 @@
   }
 
   if (!tls13_derive_session_psk(session.get(), ticket_nonce)) {
-    return 0;
+    return false;
   }
 
   // Parse out the extensions.
@@ -898,7 +899,7 @@
                             OPENSSL_ARRAY_SIZE(ext_types),
                             1 /* ignore unknown */)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
-    return 0;
+    return false;
   }
 
   if (have_early_data_info && ssl->enable_early_data) {
@@ -906,10 +907,15 @@
         CBS_len(&early_data_info) != 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      return 0;
+      return false;
     }
   }
 
+  // Generate a session ID for this session. Some callers expect all sessions to
+  // have a session ID.
+  SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id);
+  session->session_id_length = SHA256_DIGEST_LENGTH;
+
   session->ticket_age_add_valid = true;
   session->not_resumable = false;
 
@@ -920,7 +926,7 @@
     session.release();
   }
 
-  return 1;
+  return true;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/tls13_enc.cc b/src/ssl/tls13_enc.cc
index 84bc5d2..5e1f19a 100644
--- a/src/ssl/tls13_enc.cc
+++ b/src/ssl/tls13_enc.cc
@@ -30,12 +30,12 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
-static int init_key_schedule(SSL_HANDSHAKE *hs, uint16_t version,
+static bool init_key_schedule(SSL_HANDSHAKE *hs, uint16_t version,
                              const SSL_CIPHER *cipher) {
   if (!hs->transcript.InitHash(version, cipher)) {
-    return 0;
+    return false;
   }
 
   hs->hash_len = hs->transcript.DigestLen();
@@ -43,13 +43,13 @@
   // Initialize the secret to the zero key.
   OPENSSL_memset(hs->secret, 0, hs->hash_len);
 
-  return 1;
+  return true;
 }
 
-int tls13_init_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
-                            size_t psk_len) {
+bool tls13_init_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
+                             size_t psk_len) {
   if (!init_key_schedule(hs, ssl_protocol_version(hs->ssl), hs->new_cipher)) {
-    return 0;
+    return false;
   }
 
   hs->transcript.FreeBuffer();
@@ -57,8 +57,8 @@
                       psk_len, hs->secret, hs->hash_len);
 }
 
-int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
-                                  size_t psk_len) {
+bool tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *psk,
+                                   size_t psk_len) {
   SSL *const ssl = hs->ssl;
   return init_key_schedule(hs, ssl_session_protocol_version(ssl->session.get()),
                            ssl->session->cipher) &&
@@ -66,10 +66,11 @@
                       psk_len, hs->secret, hs->hash_len);
 }
 
-static int hkdf_expand_label(uint8_t *out, const EVP_MD *digest,
-                             const uint8_t *secret, size_t secret_len,
-                             const char *label, size_t label_len,
-                             const uint8_t *hash, size_t hash_len, size_t len) {
+static bool hkdf_expand_label(uint8_t *out, const EVP_MD *digest,
+                              const uint8_t *secret, size_t secret_len,
+                              const char *label, size_t label_len,
+                              const uint8_t *hash, size_t hash_len,
+                              size_t len) {
   static const char kTLS13LabelVersion[] = "tls13 ";
 
   ScopedCBB cbb;
@@ -85,7 +86,7 @@
       !CBB_add_u8_length_prefixed(cbb.get(), &child) ||
       !CBB_add_bytes(&child, hash, hash_len) ||
       !CBBFinishArray(cbb.get(), &hkdf_label)) {
-    return 0;
+    return false;
   }
 
   return HKDF_expand(out, len, digest, secret, secret_len, hkdf_label.data(),
@@ -94,20 +95,20 @@
 
 static const char kTLS13LabelDerived[] = "derived";
 
-int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
-                               size_t len) {
+bool tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
+                                size_t len) {
   uint8_t derive_context[EVP_MAX_MD_SIZE];
   unsigned derive_context_len;
   if (!EVP_Digest(nullptr, 0, derive_context, &derive_context_len,
                   hs->transcript.Digest(), nullptr)) {
-    return 0;
+    return false;
   }
 
   if (!hkdf_expand_label(hs->secret, hs->transcript.Digest(), hs->secret,
                          hs->hash_len, kTLS13LabelDerived,
                          strlen(kTLS13LabelDerived), derive_context,
                          derive_context_len, hs->hash_len)) {
-    return 0;
+    return false;
   }
 
   return HKDF_extract(hs->secret, &hs->hash_len, hs->transcript.Digest(), in,
@@ -116,13 +117,13 @@
 
 // derive_secret derives a secret of length |len| and writes the result in |out|
 // with the given label and the current base secret and most recently-saved
-// handshake context. It returns one on success and zero on error.
-static int derive_secret(SSL_HANDSHAKE *hs, uint8_t *out, size_t len,
-                         const char *label, size_t label_len) {
+// handshake context. It returns true on success and false on error.
+static bool derive_secret(SSL_HANDSHAKE *hs, uint8_t *out, size_t len,
+                          const char *label, size_t label_len) {
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
   if (!hs->transcript.GetHash(context_hash, &context_hash_len)) {
-    return 0;
+    return false;
   }
 
   return hkdf_expand_label(out, hs->transcript.Digest(), hs->secret,
@@ -130,15 +131,15 @@
                            context_hash_len, len);
 }
 
-int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
-                          const uint8_t *traffic_secret,
-                          size_t traffic_secret_len) {
+bool tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
+                           const uint8_t *traffic_secret,
+                           size_t traffic_secret_len) {
   const SSL_SESSION *session = SSL_get_session(ssl);
   uint16_t version = ssl_session_protocol_version(session);
 
   if (traffic_secret_len > 0xff) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
-    return 0;
+    return false;
   }
 
   // Look up cipher suite properties.
@@ -146,7 +147,7 @@
   size_t discard;
   if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, session->cipher,
                                version, SSL_is_dtls(ssl))) {
-    return 0;
+    return false;
   }
 
   const EVP_MD *digest = ssl_session_get_digest(session);
@@ -156,7 +157,7 @@
   uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
   if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len, "key",
                          3, NULL, 0, key_len)) {
-    return 0;
+    return false;
   }
 
   // Derive the IV.
@@ -164,7 +165,7 @@
   uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH];
   if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len, "iv",
                          2, NULL, 0, iv_len)) {
-    return 0;
+    return false;
   }
 
   UniquePtr<SSLAEADContext> traffic_aead =
@@ -172,16 +173,16 @@
                              session->cipher, MakeConstSpan(key, key_len),
                              Span<const uint8_t>(), MakeConstSpan(iv, iv_len));
   if (!traffic_aead) {
-    return 0;
+    return false;
   }
 
   if (direction == evp_aead_open) {
     if (!ssl->method->set_read_state(ssl, std::move(traffic_aead))) {
-      return 0;
+      return false;
     }
   } else {
     if (!ssl->method->set_write_state(ssl, std::move(traffic_aead))) {
-      return 0;
+      return false;
     }
   }
 
@@ -196,7 +197,7 @@
     ssl->s3->write_traffic_secret_len = traffic_secret_len;
   }
 
-  return 1;
+  return true;
 }
 
 
@@ -209,7 +210,7 @@
 static const char kTLS13LabelClientApplicationTraffic[] = "c ap traffic";
 static const char kTLS13LabelServerApplicationTraffic[] = "s ap traffic";
 
-int tls13_derive_early_secrets(SSL_HANDSHAKE *hs) {
+bool tls13_derive_early_secrets(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   if (!derive_secret(hs, hs->early_traffic_secret, hs->hash_len,
                      kTLS13LabelClientEarlyTraffic,
@@ -219,13 +220,13 @@
       !derive_secret(hs, ssl->s3->early_exporter_secret, hs->hash_len,
                      kTLS13LabelEarlyExporter,
                      strlen(kTLS13LabelEarlyExporter))) {
-    return 0;
+    return false;
   }
   ssl->s3->early_exporter_secret_len = hs->hash_len;
-  return 1;
+  return true;
 }
 
-int tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs) {
+bool tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   return derive_secret(hs, hs->client_handshake_secret, hs->hash_len,
                        kTLS13LabelClientHandshakeTraffic,
@@ -239,7 +240,7 @@
                         hs->server_handshake_secret, hs->hash_len);
 }
 
-int tls13_derive_application_secrets(SSL_HANDSHAKE *hs) {
+bool tls13_derive_application_secrets(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   ssl->s3->exporter_secret_len = hs->hash_len;
   return derive_secret(hs, hs->client_traffic_secret_0, hs->hash_len,
@@ -260,7 +261,7 @@
 
 static const char kTLS13LabelApplicationTraffic[] = "traffic upd";
 
-int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) {
+bool tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) {
   uint8_t *secret;
   size_t secret_len;
   if (direction == evp_aead_open) {
@@ -275,7 +276,7 @@
   if (!hkdf_expand_label(
           secret, digest, secret, secret_len, kTLS13LabelApplicationTraffic,
           strlen(kTLS13LabelApplicationTraffic), NULL, 0, secret_len)) {
-    return 0;
+    return false;
   }
 
   return tls13_set_traffic_key(ssl, direction, secret, secret_len);
@@ -283,10 +284,10 @@
 
 static const char kTLS13LabelResumption[] = "res master";
 
-int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs) {
+bool tls13_derive_resumption_secret(SSL_HANDSHAKE *hs) {
   if (hs->hash_len > SSL_MAX_MASTER_KEY_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
   hs->new_session->master_key_length = hs->hash_len;
   return derive_secret(hs, hs->new_session->master_key,
@@ -298,23 +299,23 @@
 
 // tls13_verify_data sets |out| to be the HMAC of |context| using a derived
 // Finished key for both Finished messages and the PSK binder.
-static int tls13_verify_data(const EVP_MD *digest, uint16_t version,
-                             uint8_t *out, size_t *out_len,
-                             const uint8_t *secret, size_t hash_len,
-                             uint8_t *context, size_t context_len) {
+static bool tls13_verify_data(const EVP_MD *digest, uint16_t version,
+                              uint8_t *out, size_t *out_len,
+                              const uint8_t *secret, size_t hash_len,
+                              uint8_t *context, size_t context_len) {
   uint8_t key[EVP_MAX_MD_SIZE];
   unsigned len;
   if (!hkdf_expand_label(key, digest, secret, hash_len, kTLS13LabelFinished,
                          strlen(kTLS13LabelFinished), NULL, 0, hash_len) ||
       HMAC(digest, key, hash_len, context, context_len, out, &len) == NULL) {
-    return 0;
+    return false;
   }
   *out_len = len;
-  return 1;
+  return true;
 }
 
-int tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
-                       int is_server) {
+bool tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
+                        bool is_server) {
   const uint8_t *traffic_secret;
   if (is_server) {
     traffic_secret = hs->server_handshake_secret;
@@ -345,14 +346,14 @@
 
 static const char kTLS13LabelExportKeying[] = "exporter";
 
-int tls13_export_keying_material(SSL *ssl, Span<uint8_t> out,
-                                 Span<const uint8_t> secret,
-                                 Span<const char> label,
-                                 Span<const uint8_t> context) {
+bool tls13_export_keying_material(SSL *ssl, Span<uint8_t> out,
+                                  Span<const uint8_t> secret,
+                                  Span<const char> label,
+                                  Span<const uint8_t> context) {
   if (secret.empty()) {
     assert(0);
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   const EVP_MD *digest = ssl_session_get_digest(SSL_get_session(ssl));
@@ -378,21 +379,21 @@
 
 static const char kTLS13LabelPSKBinder[] = "res binder";
 
-static int tls13_psk_binder(uint8_t *out, uint16_t version,
-                            const EVP_MD *digest, uint8_t *psk, size_t psk_len,
-                            uint8_t *context, size_t context_len,
-                            size_t hash_len) {
+static bool tls13_psk_binder(uint8_t *out, uint16_t version,
+                             const EVP_MD *digest, uint8_t *psk, size_t psk_len,
+                             uint8_t *context, size_t context_len,
+                             size_t hash_len) {
   uint8_t binder_context[EVP_MAX_MD_SIZE];
   unsigned binder_context_len;
   if (!EVP_Digest(NULL, 0, binder_context, &binder_context_len, digest, NULL)) {
-    return 0;
+    return false;
   }
 
   uint8_t early_secret[EVP_MAX_MD_SIZE] = {0};
   size_t early_secret_len;
   if (!HKDF_extract(early_secret, &early_secret_len, digest, psk, hash_len,
                     NULL, 0)) {
-    return 0;
+    return false;
   }
 
   uint8_t binder_key[EVP_MAX_MD_SIZE] = {0};
@@ -402,20 +403,20 @@
                          binder_context, binder_context_len, hash_len) ||
       !tls13_verify_data(digest, version, out, &len, binder_key, hash_len,
                          context, context_len)) {
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) {
+bool tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) {
   SSL *const ssl = hs->ssl;
   const EVP_MD *digest = ssl_session_get_digest(ssl->session.get());
   size_t hash_len = EVP_MD_size(digest);
 
   if (len < hash_len + 3) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   ScopedEVP_MD_CTX ctx;
@@ -427,7 +428,7 @@
                         hs->transcript.buffer().size()) ||
       !EVP_DigestUpdate(ctx.get(), msg, len - hash_len - 3) ||
       !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
-    return 0;
+    return false;
   }
 
   uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
@@ -435,21 +436,21 @@
                         ssl->session->master_key,
                         ssl->session->master_key_length, context, context_len,
                         hash_len)) {
-    return 0;
+    return false;
   }
 
   OPENSSL_memcpy(msg + len - hash_len, verify_data, hash_len);
-  return 1;
+  return true;
 }
 
-int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
-                            const SSLMessage &msg, CBS *binders) {
+bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
+                             const SSLMessage &msg, CBS *binders) {
   size_t hash_len = hs->transcript.DigestLen();
 
   // The message must be large enough to exclude the binders.
   if (CBS_len(&msg.raw) < CBS_len(binders) + 2) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   // Hash a ClientHello prefix up to the binders. This includes the header. For
@@ -459,7 +460,7 @@
   unsigned context_len;
   if (!EVP_Digest(CBS_data(&msg.raw), CBS_len(&msg.raw) - CBS_len(binders) - 2,
                   context, &context_len, hs->transcript.Digest(), NULL)) {
-    return 0;
+    return false;
   }
 
   uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
@@ -470,21 +471,21 @@
       // We only consider the first PSK, so compare against the first binder.
       !CBS_get_u8_length_prefixed(binders, &binder)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
-  int binder_ok =
+  bool binder_ok =
       CBS_len(&binder) == hash_len &&
       CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) == 0;
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
-  binder_ok = 1;
+  binder_ok = true;
 #endif
   if (!binder_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
-    return 0;
+    return false;
   }
 
-  return 1;
+  return true;
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc
index aba7fc0..0d82d68 100644
--- a/src/ssl/tls13_server.cc
+++ b/src/ssl/tls13_server.cc
@@ -36,7 +36,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 enum server_hs_state_t {
   state_select_parameters = 0,
@@ -716,7 +716,7 @@
 
     size_t finished_len;
     if (!tls13_finished_mac(hs, hs->expected_client_finished, &finished_len,
-                            0 /* client */)) {
+                            false /* client */)) {
       return ssl_hs_error;
     }
 
@@ -807,7 +807,7 @@
     return ssl_hs_ok;
   }
 
-  const int allow_anonymous =
+  const bool allow_anonymous =
       (hs->config->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
   SSLMessage msg;
   if (!ssl->method->get_message(ssl, &msg)) {
@@ -1028,4 +1028,4 @@
   return "TLS 1.3 server unknown";
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
diff --git a/src/ssl/tls_method.cc b/src/ssl/tls_method.cc
index 2af5171..bc9410b 100644
--- a/src/ssl/tls_method.cc
+++ b/src/ssl/tls_method.cc
@@ -65,7 +65,7 @@
 #include "internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 static void ssl3_on_handshake_complete(SSL *ssl) {
   // The handshake should have released its final message.
@@ -119,7 +119,6 @@
     ssl3_finish_message,
     ssl3_add_message,
     ssl3_add_change_cipher_spec,
-    ssl3_add_alert,
     ssl3_flush_flight,
     ssl3_on_handshake_complete,
     ssl3_set_read_state,
@@ -182,7 +181,7 @@
   ssl_noop_x509_ssl_ctx_flush_cached_client_CA,
 };
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/ssl/tls_record.cc b/src/ssl/tls_record.cc
index a2e4a20..c1f9e7f 100644
--- a/src/ssl/tls_record.cc
+++ b/src/ssl/tls_record.cc
@@ -119,7 +119,7 @@
 #include "../crypto/internal.h"
 
 
-namespace bssl {
+BSSL_NAMESPACE_BEGIN
 
 // kMaxEmptyRecords is the number of consecutive, empty records that will be
 // processed. Without this limit an attacker could send empty records at a
@@ -140,26 +140,26 @@
 
 // ssl_needs_record_splitting returns one if |ssl|'s current outgoing cipher
 // state needs record-splitting and zero otherwise.
-static int ssl_needs_record_splitting(const SSL *ssl) {
+static bool ssl_needs_record_splitting(const SSL *ssl) {
 #if !defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   return !ssl->s3->aead_write_ctx->is_null_cipher() &&
          ssl->s3->aead_write_ctx->ProtocolVersion() < TLS1_1_VERSION &&
          (ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 &&
          SSL_CIPHER_is_block_cipher(ssl->s3->aead_write_ctx->cipher());
 #else
-  return 0;
+  return false;
 #endif
 }
 
-int ssl_record_sequence_update(uint8_t *seq, size_t seq_len) {
+bool ssl_record_sequence_update(uint8_t *seq, size_t seq_len) {
   for (size_t i = seq_len - 1; i < seq_len; i--) {
     ++seq[i];
     if (seq[i] != 0) {
-      return 1;
+      return true;
     }
   }
   OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
-  return 0;
+  return false;
 }
 
 size_t ssl_record_prefix_len(const SSL *ssl) {
@@ -373,9 +373,9 @@
   return ssl_open_record_success;
 }
 
-static int do_seal_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out,
-                          uint8_t *out_suffix, uint8_t type, const uint8_t *in,
-                          const size_t in_len) {
+static bool do_seal_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out,
+                           uint8_t *out_suffix, uint8_t type, const uint8_t *in,
+                           const size_t in_len) {
   SSLAEADContext *aead = ssl->s3->aead_write_ctx.get();
   uint8_t *extra_in = NULL;
   size_t extra_in_len = 0;
@@ -390,7 +390,7 @@
   if (!aead->SuffixLen(&suffix_len, in_len, extra_in_len) ||
       !aead->CiphertextLen(&ciphertext_len, in_len, extra_in_len)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE);
-    return 0;
+    return false;
   }
 
   assert(in == out || !buffers_alias(in, in_len, out, in_len));
@@ -415,11 +415,11 @@
                          out_prefix[0], record_version, ssl->s3->write_sequence,
                          header, in, in_len, extra_in, extra_in_len) ||
       !ssl_record_sequence_update(ssl->s3->write_sequence, 8)) {
-    return 0;
+    return false;
   }
 
   ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, header);
-  return 1;
+  return true;
 }
 
 static size_t tls_seal_scatter_prefix_len(const SSL *ssl, uint8_t type,
@@ -464,7 +464,7 @@
 // returns one on success and zero on error. If enabled,
 // |tls_seal_scatter_record| implements TLS 1.0 CBC 1/n-1 record splitting and
 // may write two records concatenated.
-static int tls_seal_scatter_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out,
+static bool tls_seal_scatter_record(SSL *ssl, uint8_t *out_prefix, uint8_t *out,
                                    uint8_t *out_suffix, uint8_t type,
                                    const uint8_t *in, size_t in_len) {
   if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 &&
@@ -478,13 +478,13 @@
 
     if (!do_seal_record(ssl, out_prefix, split_body, split_suffix, type, in,
                         1)) {
-      return 0;
+      return false;
     }
 
     size_t split_record_suffix_len;
     if (!ssl->s3->aead_write_ctx->SuffixLen(&split_record_suffix_len, 1, 0)) {
       assert(false);
-      return 0;
+      return false;
     }
     const size_t split_record_len = prefix_len + 1 + split_record_suffix_len;
     assert(SSL3_RT_HEADER_LENGTH + ssl_cipher_get_record_split_len(
@@ -496,24 +496,25 @@
     uint8_t tmp_prefix[SSL3_RT_HEADER_LENGTH];
     if (!do_seal_record(ssl, tmp_prefix, out + 1, out_suffix, type, in + 1,
                         in_len - 1)) {
-      return 0;
+      return false;
     }
     assert(tls_seal_scatter_prefix_len(ssl, type, in_len) ==
            split_record_len + SSL3_RT_HEADER_LENGTH - 1);
     OPENSSL_memcpy(out_prefix + split_record_len, tmp_prefix,
                    SSL3_RT_HEADER_LENGTH - 1);
     OPENSSL_memcpy(out, tmp_prefix + SSL3_RT_HEADER_LENGTH - 1, 1);
-    return 1;
+    return true;
   }
 
   return do_seal_record(ssl, out_prefix, out, out_suffix, type, in, in_len);
 }
 
-int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len,
-                    uint8_t type, const uint8_t *in, size_t in_len) {
+bool tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len,
+                     size_t max_out_len, uint8_t type, const uint8_t *in,
+                     size_t in_len) {
   if (buffers_alias(in, in_len, out, max_out_len)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
-    return 0;
+    return false;
   }
 
   const size_t prefix_len = tls_seal_scatter_prefix_len(ssl, type, in_len);
@@ -524,22 +525,22 @@
   if (in_len + prefix_len < in_len ||
       prefix_len + in_len + suffix_len < prefix_len + in_len) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE);
-    return 0;
+    return false;
   }
   if (max_out_len < in_len + prefix_len + suffix_len) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
-    return 0;
+    return false;
   }
 
   uint8_t *prefix = out;
   uint8_t *body = out + prefix_len;
   uint8_t *suffix = body + in_len;
   if (!tls_seal_scatter_record(ssl, prefix, body, suffix, type, in, in_len)) {
-    return 0;
+    return false;
   }
 
   *out_len = prefix_len + in_len + suffix_len;
-  return 1;
+  return true;
 }
 
 enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert,
@@ -674,7 +675,7 @@
                                  in.data(), in.size());
 }
 
-}  // namespace bssl
+BSSL_NAMESPACE_END
 
 using namespace bssl;
 
diff --git a/src/third_party/fiat/CMakeLists.txt b/src/third_party/fiat/CMakeLists.txt
deleted file mode 100644
index fcc77d5..0000000
--- a/src/third_party/fiat/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  fiat
-
-  OBJECT
-
-  curve25519.c
-)
diff --git a/src/third_party/fiat/curve25519.c b/src/third_party/fiat/curve25519.c
index 60da1c8..58a5ed0 100644
--- a/src/third_party/fiat/curve25519.c
+++ b/src/third_party/fiat/curve25519.c
@@ -2960,6 +2960,11 @@
 
 int ED25519_sign(uint8_t out_sig[64], const uint8_t *message,
                  size_t message_len, const uint8_t private_key[64]) {
+  // NOTE: The documentation on this function says that it returns zero on
+  // allocation failure. While that can't happen with the current
+  // implementation, we want to reserve the ability to allocate in this
+  // implementation in the future.
+
   uint8_t az[SHA512_DIGEST_LENGTH];
   SHA512(private_key, 32, az);
 
diff --git a/src/tool/CMakeLists.txt b/src/tool/CMakeLists.txt
index 87efb09..7f34017 100644
--- a/src/tool/CMakeLists.txt
+++ b/src/tool/CMakeLists.txt
@@ -20,6 +20,8 @@
   transport_common.cc
 )
 
+add_dependencies(bssl global_target)
+
 if(APPLE OR WIN32 OR ANDROID)
   target_link_libraries(bssl ssl crypto)
 else()
diff --git a/src/util/all_tests.json b/src/util/all_tests.json
index 01d6fd0..7152ec1 100644
--- a/src/util/all_tests.json
+++ b/src/util/all_tests.json
@@ -1,5 +1,6 @@
 [
 	["crypto/crypto_test"],
+	["crypto/crypto_test", "--fork_unsafe_buffering", "--gtest_filter=RandTest.*:-RandTest.Fork"],
 	["decrepit/decrepit_test"],
 	["ssl/ssl_test"]
 ]
diff --git a/src/util/fipstools/ar.go b/src/util/ar/ar.go
similarity index 79%
rename from src/util/fipstools/ar.go
rename to src/util/ar/ar.go
index 51e7aa5..f5dee62 100644
--- a/src/util/fipstools/ar.go
+++ b/src/util/ar/ar.go
@@ -14,11 +14,12 @@
 
 // ar.go contains functions for parsing .a archive files.
 
-package main
+package ar
 
 import (
 	"bytes"
 	"errors"
+	"fmt"
 	"io"
 	"strconv"
 	"strings"
@@ -113,6 +114,33 @@
 			name = strings.TrimRight(name, "/")
 		}
 
+		// Post-processing for BSD:
+		// https://en.wikipedia.org/wiki/Ar_(Unix)#BSD_variant
+		//
+		// If the name is of the form #1/XXX, XXX identifies the length of the
+		// name, and the name itself is stored as a prefix of the data, possibly
+		// null-padded.
+
+		var namelen uint
+		n, err := fmt.Sscanf(name, "#1/%d", &namelen)
+		if err == nil && n == 1 && len(contents) >= int(namelen) {
+			name = string(contents[:namelen])
+			contents = contents[namelen:]
+
+			// Names can be null padded; find the first null (if any). Note that
+			// this also handles the case of a null followed by non-null
+			// characters. It's not clear whether those can ever show up in
+			// practice, but we might as well handle them in case they can show
+			// up.
+			var null int
+			for ; null < len(name); null++ {
+				if name[null] == 0 {
+					break
+				}
+			}
+			name = name[:null]
+		}
+
 		ret[name] = contents
 	}
 
diff --git a/src/util/bot/DEPS b/src/util/bot/DEPS
index 5dab203..5449ab0 100644
--- a/src/util/bot/DEPS
+++ b/src/util/bot/DEPS
@@ -16,7 +16,6 @@
   'chromium_git': 'https://chromium.googlesource.com',
 
   'checkout_clang': False,
-  'checkout_fuzzer': False,
   'checkout_sde': False,
   'checkout_nasm': False,
   'checkout_libcxx': False,
@@ -24,7 +23,7 @@
 
 deps = {
   'boringssl/util/bot/android_ndk': {
-    'url': Var('chromium_git') + '/android_ndk.git' + '@' + '5cd86312e794bdf542a3685c6f10cbb96072990b',
+    'url': Var('chromium_git') + '/android_ndk.git' + '@' + '4e2cea441bfd43f0863d14f57b1e1844260b9884',
     'condition': 'checkout_android',
   },
 
@@ -36,11 +35,6 @@
   'boringssl/util/bot/gyp':
     Var('chromium_git') + '/external/gyp.git' + '@' + 'd61a9397e668fa9843c4aa7da9e79460fe590bfb',
 
-  'boringssl/util/bot/libFuzzer': {
-    'url': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git' + '@' + '658ff786a213703ff0df6ba4a288e9a1e218c074',
-    'condition': 'checkout_fuzzer',
-  },
-
   # Update the following revisions from
   # https://chromium.googlesource.com/chromium/buildtools/+/master/DEPS
   'boringssl/util/bot/libcxx': {
diff --git a/src/util/bot/go/bootstrap.py b/src/util/bot/go/bootstrap.py
index eb6eed9..9dbabe5 100755
--- a/src/util/bot/go/bootstrap.py
+++ b/src/util/bot/go/bootstrap.py
@@ -45,7 +45,7 @@
 EXE_SFX = '.exe' if sys.platform == 'win32' else ''
 
 # Pinned version of Go toolset to download.
-TOOLSET_VERSION = 'go1.10.3'
+TOOLSET_VERSION = 'go1.11'
 
 # Platform dependent portion of a download URL. See http://golang.org/dl/.
 TOOLSET_VARIANTS = {
diff --git a/src/util/doc.go b/src/util/doc.go
index 040ac79..33bcc66 100644
--- a/src/util/doc.go
+++ b/src/util/doc.go
@@ -82,6 +82,19 @@
 	return strings.HasPrefix(line, commentStart) || strings.HasPrefix(line, lineComment)
 }
 
+func commentSubject(line string) string {
+	if strings.HasPrefix(line, "A ") {
+		line = line[len("A "):]
+	} else if strings.HasPrefix(line, "An ") {
+		line = line[len("An "):]
+	}
+	idx := strings.IndexAny(line, " ,")
+	if idx < 0 {
+		return line
+	}
+	return line[:idx]
+}
+
 func extractComment(lines []string, lineNo int) (comment []string, rest []string, restLineNo int, err error) {
 	if len(lines) == 0 {
 		return nil, lines, lineNo, nil
@@ -426,17 +439,22 @@
 				// As a matter of style, comments should start
 				// with the name of the thing that they are
 				// commenting on. We make an exception here for
-				// #defines (because we often have blocks of
-				// them) and collective comments, which are
-				// detected by starting with “The” or “These”.
+				// collective comments, which are detected by
+				// starting with “The” or “These”.
 				if len(comment) > 0 &&
-					!strings.HasPrefix(comment[0], name) &&
-					!strings.HasPrefix(comment[0], "A "+name) &&
-					!strings.HasPrefix(comment[0], "An "+name) &&
-					!strings.HasPrefix(decl, "#define ") &&
+					len(name) > 0 &&
 					!strings.HasPrefix(comment[0], "The ") &&
 					!strings.HasPrefix(comment[0], "These ") {
-					return nil, fmt.Errorf("Comment for %q doesn't seem to match line %s:%d\n", name, path, declLineNo)
+					subject := commentSubject(comment[0])
+					ok := subject == name
+					if l := len(subject); l > 0 && subject[l-1] == '*' {
+						// Groups of names, notably #defines, are often
+						// denoted with a wildcard.
+						ok = strings.HasPrefix(name, subject[:l-1])
+					}
+					if !ok {
+						return nil, fmt.Errorf("comment for %q doesn't seem to match line %s:%d\n", name, path, declLineNo)
+					}
 				}
 				anchor := sanitizeAnchor(name)
 				// TODO(davidben): Enforce uniqueness. This is
diff --git a/src/util/fipstools/delocate.go b/src/util/fipstools/delocate/delocate.go
similarity index 99%
rename from src/util/fipstools/delocate.go
rename to src/util/fipstools/delocate/delocate.go
index d58e5be..4734285 100644
--- a/src/util/fipstools/delocate.go
+++ b/src/util/fipstools/delocate/delocate.go
@@ -25,6 +25,9 @@
 	"sort"
 	"strconv"
 	"strings"
+
+	"boringssl.googlesource.com/boringssl/util/ar"
+	"boringssl.googlesource.com/boringssl/util/fipstools/fipscommon"
 )
 
 // inputFile represents a textual assembly file.
@@ -1405,7 +1408,7 @@
 	w.WriteString(".type BORINGSSL_bcm_text_hash, @object\n")
 	w.WriteString(".size BORINGSSL_bcm_text_hash, 64\n")
 	w.WriteString("BORINGSSL_bcm_text_hash:\n")
-	for _, b := range uninitHashValue {
+	for _, b := range fipscommon.UninitHashValue {
 		w.WriteString(".byte 0x" + strconv.FormatUint(uint64(b), 16) + "\n")
 	}
 
@@ -1423,7 +1426,7 @@
 			}
 			defer arFile.Close()
 
-			ar, err := ParseAR(arFile)
+			ar, err := ar.ParseAR(arFile)
 			if err != nil {
 				return err
 			}
diff --git a/src/util/fipstools/delocate.peg b/src/util/fipstools/delocate/delocate.peg
similarity index 100%
rename from src/util/fipstools/delocate.peg
rename to src/util/fipstools/delocate/delocate.peg
diff --git a/src/util/fipstools/delocate.peg.go b/src/util/fipstools/delocate/delocate.peg.go
similarity index 100%
rename from src/util/fipstools/delocate.peg.go
rename to src/util/fipstools/delocate/delocate.peg.go
diff --git a/src/util/fipstools/delocate_test.go b/src/util/fipstools/delocate/delocate_test.go
similarity index 100%
rename from src/util/fipstools/delocate_test.go
rename to src/util/fipstools/delocate/delocate_test.go
diff --git a/src/util/fipstools/testdata/ppc64le-GlobalEntry/in.s b/src/util/fipstools/delocate/testdata/ppc64le-GlobalEntry/in.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-GlobalEntry/in.s
rename to src/util/fipstools/delocate/testdata/ppc64le-GlobalEntry/in.s
diff --git a/src/util/fipstools/testdata/ppc64le-GlobalEntry/out.s b/src/util/fipstools/delocate/testdata/ppc64le-GlobalEntry/out.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-GlobalEntry/out.s
rename to src/util/fipstools/delocate/testdata/ppc64le-GlobalEntry/out.s
diff --git a/src/util/fipstools/testdata/ppc64le-LoadToR0/in.s b/src/util/fipstools/delocate/testdata/ppc64le-LoadToR0/in.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-LoadToR0/in.s
rename to src/util/fipstools/delocate/testdata/ppc64le-LoadToR0/in.s
diff --git a/src/util/fipstools/testdata/ppc64le-LoadToR0/out.s b/src/util/fipstools/delocate/testdata/ppc64le-LoadToR0/out.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-LoadToR0/out.s
rename to src/util/fipstools/delocate/testdata/ppc64le-LoadToR0/out.s
diff --git a/src/util/fipstools/testdata/ppc64le-Sample/in.s b/src/util/fipstools/delocate/testdata/ppc64le-Sample/in.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-Sample/in.s
rename to src/util/fipstools/delocate/testdata/ppc64le-Sample/in.s
diff --git a/src/util/fipstools/testdata/ppc64le-Sample/out.s b/src/util/fipstools/delocate/testdata/ppc64le-Sample/out.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-Sample/out.s
rename to src/util/fipstools/delocate/testdata/ppc64le-Sample/out.s
diff --git a/src/util/fipstools/testdata/ppc64le-Sample2/in.s b/src/util/fipstools/delocate/testdata/ppc64le-Sample2/in.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-Sample2/in.s
rename to src/util/fipstools/delocate/testdata/ppc64le-Sample2/in.s
diff --git a/src/util/fipstools/testdata/ppc64le-Sample2/out.s b/src/util/fipstools/delocate/testdata/ppc64le-Sample2/out.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-Sample2/out.s
rename to src/util/fipstools/delocate/testdata/ppc64le-Sample2/out.s
diff --git a/src/util/fipstools/testdata/ppc64le-TOCWithOffset/in.s b/src/util/fipstools/delocate/testdata/ppc64le-TOCWithOffset/in.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-TOCWithOffset/in.s
rename to src/util/fipstools/delocate/testdata/ppc64le-TOCWithOffset/in.s
diff --git a/src/util/fipstools/testdata/ppc64le-TOCWithOffset/out.s b/src/util/fipstools/delocate/testdata/ppc64le-TOCWithOffset/out.s
similarity index 100%
rename from src/util/fipstools/testdata/ppc64le-TOCWithOffset/out.s
rename to src/util/fipstools/delocate/testdata/ppc64le-TOCWithOffset/out.s
diff --git a/src/util/fipstools/testdata/x86_64-BSS/in.s b/src/util/fipstools/delocate/testdata/x86_64-BSS/in.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-BSS/in.s
rename to src/util/fipstools/delocate/testdata/x86_64-BSS/in.s
diff --git a/src/util/fipstools/testdata/x86_64-BSS/out.s b/src/util/fipstools/delocate/testdata/x86_64-BSS/out.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-BSS/out.s
rename to src/util/fipstools/delocate/testdata/x86_64-BSS/out.s
diff --git a/src/util/fipstools/testdata/x86_64-Basic/in.s b/src/util/fipstools/delocate/testdata/x86_64-Basic/in.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-Basic/in.s
rename to src/util/fipstools/delocate/testdata/x86_64-Basic/in.s
diff --git a/src/util/fipstools/testdata/x86_64-Basic/out.s b/src/util/fipstools/delocate/testdata/x86_64-Basic/out.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-Basic/out.s
rename to src/util/fipstools/delocate/testdata/x86_64-Basic/out.s
diff --git a/src/util/fipstools/testdata/x86_64-GOTRewrite/in.s b/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/in.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-GOTRewrite/in.s
rename to src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/in.s
diff --git a/src/util/fipstools/testdata/x86_64-GOTRewrite/out.s b/src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/out.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-GOTRewrite/out.s
rename to src/util/fipstools/delocate/testdata/x86_64-GOTRewrite/out.s
diff --git a/src/util/fipstools/testdata/x86_64-LabelRewrite/in1.s b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in1.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-LabelRewrite/in1.s
rename to src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in1.s
diff --git a/src/util/fipstools/testdata/x86_64-LabelRewrite/in2.s b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in2.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-LabelRewrite/in2.s
rename to src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/in2.s
diff --git a/src/util/fipstools/testdata/x86_64-LabelRewrite/out.s b/src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/out.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-LabelRewrite/out.s
rename to src/util/fipstools/delocate/testdata/x86_64-LabelRewrite/out.s
diff --git a/src/util/fipstools/testdata/x86_64-Sections/in.s b/src/util/fipstools/delocate/testdata/x86_64-Sections/in.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-Sections/in.s
rename to src/util/fipstools/delocate/testdata/x86_64-Sections/in.s
diff --git a/src/util/fipstools/testdata/x86_64-Sections/out.s b/src/util/fipstools/delocate/testdata/x86_64-Sections/out.s
similarity index 100%
rename from src/util/fipstools/testdata/x86_64-Sections/out.s
rename to src/util/fipstools/delocate/testdata/x86_64-Sections/out.s
diff --git a/src/util/fipstools/const.go b/src/util/fipstools/fipscommon/const.go
similarity index 91%
rename from src/util/fipstools/const.go
rename to src/util/fipstools/fipscommon/const.go
index 2e009ac..5693414 100644
--- a/src/util/fipstools/const.go
+++ b/src/util/fipstools/fipscommon/const.go
@@ -12,11 +12,11 @@
 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
-package main
+package fipscommon
 
-// uninitHashValue is the default hash value that we inject into the module.
+// UninitHashValue is the default hash value that we inject into the module.
 // This value need only be distinct, i.e. so that we can safely
 // search-and-replace it in an object file.
-var uninitHashValue = [64]byte{
+var UninitHashValue = [64]byte{
 	0xae, 0x2c, 0xea, 0x2a, 0xbd, 0xa6, 0xf3, 0xec, 0x97, 0x7f, 0x9b, 0xf6, 0x94, 0x9a, 0xfc, 0x83, 0x68, 0x27, 0xcb, 0xa0, 0xa0, 0x9f, 0x6b, 0x6f, 0xde, 0x52, 0xcd, 0xe2, 0xcd, 0xff, 0x31, 0x80, 0xa2, 0xd4, 0xc3, 0x66, 0x0f, 0xc2, 0x6a, 0x7b, 0xf4, 0xbe, 0x39, 0xa2, 0xd7, 0x25, 0xdb, 0x21, 0x98, 0xe9, 0xd5, 0x53, 0xbf, 0x5c, 0x32, 0x06, 0x83, 0x34, 0x0c, 0x65, 0x89, 0x52, 0xbd, 0x1f,
 }
diff --git a/src/util/fipstools/inject-hash.go b/src/util/fipstools/inject_hash/inject_hash.go
similarity index 92%
rename from src/util/fipstools/inject-hash.go
rename to src/util/fipstools/inject_hash/inject_hash.go
index 688024d..29307c0 100644
--- a/src/util/fipstools/inject-hash.go
+++ b/src/util/fipstools/inject_hash/inject_hash.go
@@ -12,7 +12,7 @@
 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
-// inject-hash parses an archive containing a file object file. It finds a FIPS
+// inject_hash parses an archive containing a file object file. It finds a FIPS
 // module inside that object, calculates its hash and replaces the default hash
 // value in the object with the calculated value.
 package main
@@ -28,6 +28,9 @@
 	"io"
 	"io/ioutil"
 	"os"
+
+	"boringssl.googlesource.com/boringssl/util/ar"
+	"boringssl.googlesource.com/boringssl/util/fipstools/fipscommon"
 )
 
 func do(outPath, oInput string, arInput string) error {
@@ -43,7 +46,7 @@
 		}
 		defer arFile.Close()
 
-		ar, err := ParseAR(arFile)
+		ar, err := ar.ParseAR(arFile)
 		if err != nil {
 			return err
 		}
@@ -145,12 +148,12 @@
 	// Replace the default hash value in the object with the calculated
 	// value and write it out.
 
-	offset := bytes.Index(objectBytes, uninitHashValue[:])
+	offset := bytes.Index(objectBytes, fipscommon.UninitHashValue[:])
 	if offset < 0 {
 		return errors.New("did not find uninitialised hash value in object file")
 	}
 
-	if bytes.Index(objectBytes[offset+1:], uninitHashValue[:]) >= 0 {
+	if bytes.Index(objectBytes[offset+1:], fipscommon.UninitHashValue[:]) >= 0 {
 		return errors.New("found two occurrences of uninitialised hash value in object file")
 	}
 
diff --git a/src/util/godeps.go b/src/util/godeps.go
new file mode 100644
index 0000000..960faa4
--- /dev/null
+++ b/src/util/godeps.go
@@ -0,0 +1,203 @@
+// Copyright (c) 2018, 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.
+
+// godeps prints out dependencies of a package in either CMake or Make depfile
+// format, for incremental rebuilds.
+//
+// The depfile format is preferred. It works correctly when new files are added.
+// However, CMake only supports depfiles for custom commands with Ninja and
+// starting CMake 3.7. For other configurations, we also support CMake's format,
+// but CMake must be rerun when file lists change.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"go/build"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+)
+
+var (
+	format  = flag.String("format", "cmake", "The format to output to, either 'cmake' or 'depfile'")
+	mainPkg = flag.String("pkg", "", "The package to print dependencies for")
+	target  = flag.String("target", "", "The name of the output file")
+	out     = flag.String("out", "", "The path to write the output to. If unset, this is stdout")
+)
+
+func cMakeQuote(in string) string {
+	// See https://cmake.org/cmake/help/v3.0/manual/cmake-language.7.html#quoted-argument
+	var b strings.Builder
+	b.Grow(len(in))
+	// Iterate over in as bytes.
+	for i := 0; i < len(in); i++ {
+		switch c := in[i]; c {
+		case '\\', '"':
+			b.WriteByte('\\')
+			b.WriteByte(c)
+		case '\t':
+			b.WriteString("\\t")
+		case '\r':
+			b.WriteString("\\r")
+		case '\n':
+			b.WriteString("\\n")
+		default:
+			b.WriteByte(in[i])
+		}
+	}
+	return b.String()
+}
+
+func writeCMake(outFile *os.File, files []string) error {
+	for i, file := range files {
+		if i != 0 {
+			if _, err := outFile.WriteString(";"); err != nil {
+				return err
+			}
+		}
+		if _, err := outFile.WriteString(cMakeQuote(file)); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func makeQuote(in string) string {
+	// See https://www.gnu.org/software/make/manual/make.html#Rule-Syntax
+	var b strings.Builder
+	b.Grow(len(in))
+	// Iterate over in as bytes.
+	for i := 0; i < len(in); i++ {
+		switch c := in[i]; c {
+		case '$':
+			b.WriteString("$$")
+		case '#', '\\', ' ':
+			b.WriteByte('\\')
+			b.WriteByte(c)
+		default:
+			b.WriteByte(c)
+		}
+	}
+	return b.String()
+}
+
+func writeDepfile(outFile *os.File, files []string) error {
+	if _, err := fmt.Fprintf(outFile, "%s:", makeQuote(*target)); err != nil {
+		return err
+	}
+	for _, file := range files {
+		if _, err := fmt.Fprintf(outFile, " %s", makeQuote(file)); err != nil {
+			return err
+		}
+	}
+	_, err := outFile.WriteString("\n")
+	return err
+}
+
+func appendPrefixed(list, newFiles []string, prefix string) []string {
+	for _, file := range newFiles {
+		list = append(list, filepath.Join(prefix, file))
+	}
+	return list
+}
+
+func main() {
+	flag.Parse()
+
+	if len(*mainPkg) == 0 {
+		fmt.Fprintf(os.Stderr, "-pkg argument is required.\n")
+		os.Exit(1)
+	}
+
+	var isDepfile bool
+	switch *format {
+	case "depfile":
+		isDepfile = true
+	case "cmake":
+		isDepfile = false
+	default:
+		fmt.Fprintf(os.Stderr, "Unknown format: %q\n", *format)
+		os.Exit(1)
+	}
+
+	if isDepfile && len(*target) == 0 {
+		fmt.Fprintf(os.Stderr, "-target argument is required for depfile.\n")
+		os.Exit(1)
+	}
+
+	done := make(map[string]struct{})
+	var files []string
+	var recurse func(pkgName string) error
+	recurse = func(pkgName string) error {
+		pkg, err := build.Default.Import(pkgName, ".", 0)
+		if err != nil {
+			return err
+		}
+
+		// Skip standard packages.
+		if pkg.Goroot {
+			return nil
+		}
+
+		// Skip already-visited packages.
+		if _, ok := done[pkg.Dir]; ok {
+			return nil
+		}
+		done[pkg.Dir] = struct{}{}
+
+		files = appendPrefixed(files, pkg.GoFiles, pkg.Dir)
+		files = appendPrefixed(files, pkg.CgoFiles, pkg.Dir)
+		// Include ignored Go files. A subsequent change may cause them
+		// to no longer be ignored.
+		files = appendPrefixed(files, pkg.IgnoredGoFiles, pkg.Dir)
+
+		// Recurse into imports.
+		for _, importName := range pkg.Imports {
+			if err := recurse(importName); err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+	if err := recurse(*mainPkg); err != nil {
+		fmt.Fprintf(os.Stderr, "Error getting dependencies: %s\n", err)
+		os.Exit(1)
+	}
+
+	sort.Strings(files)
+
+	outFile := os.Stdout
+	if len(*out) != 0 {
+		var err error
+		outFile, err = os.Create(*out)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Error writing output: %s\n", err)
+			os.Exit(1)
+		}
+		defer outFile.Close()
+	}
+
+	var err error
+	if isDepfile {
+		err = writeDepfile(outFile, files)
+	} else {
+		err = writeCMake(outFile, files)
+	}
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing output: %s\n", err)
+		os.Exit(1)
+	}
+}
diff --git a/src/util/make_prefix_headers.go b/src/util/make_prefix_headers.go
new file mode 100644
index 0000000..a5e5441
--- /dev/null
+++ b/src/util/make_prefix_headers.go
@@ -0,0 +1,216 @@
+// Copyright (c) 2018, 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.
+
+// This program takes a file containing newline-separated symbols, and generates
+// boringssl_prefix_symbols.h, boringssl_prefix_symbols_asm.h, and
+// boringssl_prefix_symbols_nasm.inc. These header files can be used to build
+// BoringSSL with a prefix for all symbols in order to avoid symbol name
+// conflicts when linking a project with multiple copies of BoringSSL; see
+// BUILDING.md for more details.
+
+// TODO(joshlf): For platforms which support it, use '#pragma redefine_extname'
+// instead of a custom macro. This avoids the need for a custom macro, but also
+// ensures that our renaming won't conflict with symbols defined and used by our
+// consumers (the "HMAC" problem). An example of this approach can be seen in
+// IllumOS' fork of OpenSSL:
+// https://github.com/joyent/illumos-extra/blob/master/openssl1x/sunw_prefix.h
+
+package main
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var out = flag.String("out", ".", "Path to a directory where the outputs will be written")
+
+// Read newline-separated symbols from a file, ignoring any comments started
+// with '#'.
+func readSymbols(path string) ([]string, error) {
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	scanner := bufio.NewScanner(f)
+	var ret []string
+	for scanner.Scan() {
+		line := scanner.Text()
+		if idx := strings.IndexByte(line, '#'); idx >= 0 {
+			line = line[:idx]
+		}
+		line = strings.TrimSpace(line)
+		if len(line) == 0 {
+			continue
+		}
+		ret = append(ret, line)
+	}
+	if err := scanner.Err(); err != nil {
+		return nil, err
+	}
+	return ret, nil
+}
+
+func writeCHeader(symbols []string, path string) error {
+	f, err := os.Create(path)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	if _, err := f.WriteString(`// Copyright (c) 2018, 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.
+
+// BORINGSSL_ADD_PREFIX pastes two identifiers into one. It performs one
+// iteration of macro expansion on its arguments before pasting.
+#define BORINGSSL_ADD_PREFIX(a, b) BORINGSSL_ADD_PREFIX_INNER(a, b)
+#define BORINGSSL_ADD_PREFIX_INNER(a, b) a ## _ ## b
+
+`); err != nil {
+		return err
+	}
+
+	for _, symbol := range symbols {
+		if _, err := fmt.Fprintf(f, "#define %s BORINGSSL_ADD_PREFIX(BORINGSSL_PREFIX, %s)\n", symbol, symbol); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func writeASMHeader(symbols []string, path string) error {
+	f, err := os.Create(path)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	if _, err := f.WriteString(`// Copyright (c) 2018, 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.
+
+#if !defined(__APPLE__)
+#include <boringssl_prefix_symbols.h>
+#else
+// On iOS and macOS, we need to treat assembly symbols differently from other
+// symbols. The linker expects symbols to be prefixed with an underscore.
+// Perlasm thus generates symbol with this underscore applied. Our macros must,
+// in turn, incorporate it.
+#define BORINGSSL_ADD_PREFIX_MAC_ASM(a, b) BORINGSSL_ADD_PREFIX_INNER_MAC_ASM(a, b)
+#define BORINGSSL_ADD_PREFIX_INNER_MAC_ASM(a, b) _ ## a ## _ ## b
+
+`); err != nil {
+		return err
+	}
+
+	for _, symbol := range symbols {
+		if _, err := fmt.Fprintf(f, "#define _%s BORINGSSL_ADD_PREFIX_MAC_ASM(BORINGSSL_PREFIX, %s)\n", symbol, symbol); err != nil {
+			return err
+		}
+	}
+
+	_, err = fmt.Fprintf(f, "#endif\n")
+	return nil
+}
+
+func writeNASMHeader(symbols []string, path string) error {
+	f, err := os.Create(path)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+
+	// NASM uses a different syntax from the C preprocessor.
+	if _, err := f.WriteString(`; Copyright (c) 2018, 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.
+
+`); err != nil {
+		return err
+	}
+
+	for _, symbol := range symbols {
+		if _, err := fmt.Fprintf(f, "%%define %s BORINGSSL_PREFIX %%+ %s\n", symbol, symbol); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func main() {
+	flag.Parse()
+	if flag.NArg() != 1 {
+		fmt.Fprintf(os.Stderr, "Usage: %s [-out OUT] SYMBOLS\n", os.Args[0])
+		os.Exit(1)
+	}
+
+	symbols, err := readSymbols(flag.Arg(0))
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error reading symbols: %s\n", err)
+		os.Exit(1)
+	}
+
+	if err := writeCHeader(symbols, filepath.Join(*out, "boringssl_prefix_symbols.h")); err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing boringssl_prefix_symbols.h: %s\n", err)
+		os.Exit(1)
+	}
+
+	if err := writeASMHeader(symbols, filepath.Join(*out, "boringssl_prefix_symbols_asm.h")); err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing boringssl_prefix_symbols_asm.h: %s\n", err)
+		os.Exit(1)
+	}
+
+	if err := writeNASMHeader(symbols, filepath.Join(*out, "boringssl_prefix_symbols_nasm.inc")); err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing boringssl_prefix_symbols_nasm.inc: %s\n", err)
+		os.Exit(1)
+	}
+
+}
diff --git a/src/util/read_symbols.go b/src/util/read_symbols.go
new file mode 100644
index 0000000..5e3a177
--- /dev/null
+++ b/src/util/read_symbols.go
@@ -0,0 +1,170 @@
+// Copyright (c) 2018, 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.
+
+// read_symbols.go scans one or more .a files and, for each object contained in
+// the .a files, reads the list of symbols in that object file.
+package main
+
+import (
+	"bytes"
+	"debug/elf"
+	"debug/macho"
+	"flag"
+	"fmt"
+	"os"
+	"runtime"
+	"sort"
+	"strings"
+
+	"boringssl.googlesource.com/boringssl/util/ar"
+)
+
+const (
+	ObjFileFormatELF   = "elf"
+	ObjFileFormatMachO = "macho"
+)
+
+var outFlag = flag.String("out", "-", "File to write output symbols")
+var objFileFormat = flag.String("obj-file-format", defaultObjFileFormat(runtime.GOOS), "Object file format to expect (options are elf, macho)")
+
+func defaultObjFileFormat(goos string) string {
+	switch goos {
+	case "linux":
+		return ObjFileFormatELF
+	case "darwin":
+		return ObjFileFormatMachO
+	default:
+		// By returning a value here rather than panicking, the user can still
+		// cross-compile from an unsupported platform to a supported platform by
+		// overriding this default with a flag. If the user doesn't provide the
+		// flag, we will panic during flag parsing.
+		return "unsupported"
+	}
+}
+
+func main() {
+	flag.Parse()
+	if flag.NArg() < 1 {
+		fmt.Fprintf(os.Stderr, "Usage: %s [-out OUT] [-obj-file-format FORMAT] ARCHIVE_FILE [ARCHIVE_FILE [...]]\n", os.Args[0])
+		os.Exit(1)
+	}
+	archiveFiles := flag.Args()
+
+	out := os.Stdout
+	if *outFlag != "-" {
+		var err error
+		out, err = os.Create(*outFlag)
+		nilOrPanic(err, "failed to open output file")
+		defer out.Close()
+	}
+
+	var symbols []string
+	// Only add first instance of any symbol; keep track of them in this map.
+	added := make(map[string]bool)
+	for _, archive := range archiveFiles {
+		f, err := os.Open(archive)
+		nilOrPanic(err, "failed to open archive file %s", archive)
+		objectFiles, err := ar.ParseAR(f)
+		nilOrPanic(err, "failed to read archive file %s", archive)
+
+		for name, contents := range objectFiles {
+			if !strings.HasSuffix(name, ".o") {
+				continue
+			}
+			for _, s := range listSymbols(name, contents) {
+				if !added[s] {
+					added[s] = true
+					symbols = append(symbols, s)
+				}
+			}
+		}
+	}
+	sort.Strings(symbols)
+	for _, s := range symbols {
+		// Filter out C++ mangled names.
+		prefix := "_Z"
+		if runtime.GOOS == "darwin" {
+			prefix = "__Z"
+		}
+		if !strings.HasPrefix(s, prefix) {
+			fmt.Fprintln(out, s)
+		}
+	}
+}
+
+// listSymbols lists the exported symbols from an object file.
+func listSymbols(name string, contents []byte) []string {
+	switch *objFileFormat {
+	case ObjFileFormatELF:
+		return listSymbolsELF(name, contents)
+	case ObjFileFormatMachO:
+		return listSymbolsMachO(name, contents)
+	default:
+		panic(fmt.Errorf("unsupported object file format %v", *objFileFormat))
+	}
+}
+
+func listSymbolsELF(name string, contents []byte) []string {
+	f, err := elf.NewFile(bytes.NewReader(contents))
+	nilOrPanic(err, "failed to parse ELF file %s", name)
+	syms, err := f.Symbols()
+	nilOrPanic(err, "failed to read symbol names from ELF file %s", name)
+
+	var names []string
+	for _, sym := range syms {
+		// Only include exported, defined symbols
+		if elf.ST_BIND(sym.Info) != elf.STB_LOCAL && sym.Section != elf.SHN_UNDEF {
+			names = append(names, sym.Name)
+		}
+	}
+	return names
+}
+
+func listSymbolsMachO(name string, contents []byte) []string {
+	f, err := macho.NewFile(bytes.NewReader(contents))
+	nilOrPanic(err, "failed to parse Mach-O file %s", name)
+	if f.Symtab == nil {
+		return nil
+	}
+	var names []string
+	for _, sym := range f.Symtab.Syms {
+		// Source: https://opensource.apple.com/source/xnu/xnu-3789.51.2/EXTERNAL_HEADERS/mach-o/nlist.h.auto.html
+		const (
+			N_PEXT uint8 = 0x10 // Private external symbol bit
+			N_EXT  uint8 = 0x01 // External symbol bit, set for external symbols
+			N_TYPE uint8 = 0x0e // mask for the type bits
+
+			N_UNDF uint8 = 0x0 // undefined, n_sect == NO_SECT
+			N_ABS  uint8 = 0x2 // absolute, n_sect == NO_SECT
+			N_SECT uint8 = 0xe // defined in section number n_sect
+			N_PBUD uint8 = 0xc // prebound undefined (defined in a dylib)
+			N_INDR uint8 = 0xa // indirect
+		)
+
+		// Only include exported, defined symbols.
+		if sym.Type&N_EXT != 0 && sym.Type&N_TYPE != N_UNDF {
+			if len(sym.Name) == 0 || sym.Name[0] != '_' {
+				panic(fmt.Errorf("unexpected symbol without underscore prefix: %v", sym.Name))
+			}
+			names = append(names, sym.Name[1:])
+		}
+	}
+	return names
+}
+
+func nilOrPanic(err error, f string, args ...interface{}) {
+	if err != nil {
+		panic(fmt.Errorf(f+": %v", append(args, err)...))
+	}
+}
diff --git a/win-x86_64/crypto/chacha/chacha-x86_64.asm b/win-x86_64/crypto/chacha/chacha-x86_64.asm
index cb36246..712c344 100644
--- a/win-x86_64/crypto/chacha/chacha-x86_64.asm
+++ b/win-x86_64/crypto/chacha/chacha-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm b/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm
index 56dc206..7698cc5 100644
--- a/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm
+++ b/win-x86_64/crypto/cipher_extra/aes128gcmsiv-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.data data align=8
 
 
diff --git a/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm b/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm
index ab8cf92..a62a346 100644
--- a/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm
+++ b/win-x86_64/crypto/cipher_extra/chacha20_poly1305_x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 global	dummy_chacha20_poly1305_asm
 
 dummy_chacha20_poly1305_asm:
diff --git a/win-x86_64/crypto/fipsmodule/aes-x86_64.asm b/win-x86_64/crypto/fipsmodule/aes-x86_64.asm
index 6133a94..7edf417 100644
--- a/win-x86_64/crypto/fipsmodule/aes-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/aes-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm b/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm
index 63bcd48..cfdbe7c 100644
--- a/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/aesni-gcm-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm b/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm
index 4abe3a2..e9af4df 100644
--- a/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/aesni-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 EXTERN	OPENSSL_ia32cap_P
diff --git a/win-x86_64/crypto/fipsmodule/bsaes-x86_64.asm b/win-x86_64/crypto/fipsmodule/bsaes-x86_64.asm
index afd0dcd..57e3156 100644
--- a/win-x86_64/crypto/fipsmodule/bsaes-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/bsaes-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm b/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm
index 5b2d178..230f42c 100644
--- a/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/ghash-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 EXTERN	OPENSSL_ia32cap_P
diff --git a/win-x86_64/crypto/fipsmodule/md5-x86_64.asm b/win-x86_64/crypto/fipsmodule/md5-x86_64.asm
index 0e9d2c6..2c799de 100644
--- a/win-x86_64/crypto/fipsmodule/md5-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/md5-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 ALIGN	16
diff --git a/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm b/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm
index 8027c04..ad578b9 100644
--- a/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm
+++ b/win-x86_64/crypto/fipsmodule/p256-x86_64-asm.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 EXTERN	OPENSSL_ia32cap_P
diff --git a/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm b/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm
index 5b08e22..db379dd 100644
--- a/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/rdrand-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm b/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm
index 0c6f6e1..155c5e4 100644
--- a/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm
+++ b/win-x86_64/crypto/fipsmodule/rsaz-avx2.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm b/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm
index 229faab..cec6e2b 100644
--- a/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/sha1-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 EXTERN	OPENSSL_ia32cap_P
diff --git a/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm b/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm
index b78ff9f..5c78767 100644
--- a/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/sha256-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm b/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm
index 2c45730..6447391 100644
--- a/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/sha512-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm b/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm
index 3edde9f..015c0b2 100644
--- a/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm
+++ b/win-x86_64/crypto/fipsmodule/vpaes-x86_64.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/x86_64-mont.asm b/win-x86_64/crypto/fipsmodule/x86_64-mont.asm
index 5385e26..d28d117 100644
--- a/win-x86_64/crypto/fipsmodule/x86_64-mont.asm
+++ b/win-x86_64/crypto/fipsmodule/x86_64-mont.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64
 
 
diff --git a/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm b/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm
index 4902a75..8cbce47 100644
--- a/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm
+++ b/win-x86_64/crypto/fipsmodule/x86_64-mont5.asm
@@ -2,6 +2,10 @@
 %define XMMWORD
 %define YMMWORD
 %define ZMMWORD
+
+%ifdef BORINGSSL_PREFIX
+%include "boringssl_prefix_symbols_nasm.inc"
+%endif
 section	.text code align=64